/*
* Copyright (C) 2008-2012 OMRON SOFTWARE Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jp.co.omronsoft.openwnn;
import jp.co.omronsoft.openwnn.EN.*;
import android.content.SharedPreferences;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Message;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.method.MetaKeyKeyListener;
import android.text.style.BackgroundColorSpan;
import android.text.style.CharacterStyle;
import android.text.style.ForegroundColorSpan;
import android.text.style.UnderlineSpan;
import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
/**
* The OpenWnn English IME class.
*
* @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD. All Rights Reserved.
*/
public class OpenWnnEN extends OpenWnn {
/** A space character */
private static final char[] SPACE = {' '};
/** Character style of underline */
private static final CharacterStyle SPAN_UNDERLINE = new UnderlineSpan();
/** Highlight color style for the selected string */
private static final CharacterStyle SPAN_EXACT_BGCOLOR_HL = new BackgroundColorSpan(0xFF66CDAA);
/** Highlight color style for the composing text */
private static final CharacterStyle SPAN_REMAIN_BGCOLOR_HL = new BackgroundColorSpan(0xFFF0FFFF);
/** Highlight text color */
private static final CharacterStyle SPAN_TEXTCOLOR = new ForegroundColorSpan(0xFF000000);
/** A private area code(ALT+SHIFT+X) to be ignore (G1 specific). */
private static final int PRIVATE_AREA_CODE = 61184;
/** Never move cursor in to the composing text (adapting to IMF's specification change) */
private static final boolean FIX_CURSOR_TEXT_END = true;
/** Spannable string for the composing text */
protected SpannableStringBuilder mDisplayText;
/** Characters treated as a separator */
private String mWordSeparators;
/** Previous event's code */
private int mPreviousEventCode;
/** Array of words from the user dictionary */
private WnnWord[] mUserDictionaryWords = null;
/** The converter for English prediction/spell correction */
private OpenWnnEngineEN mConverterEN;
/** The symbol list generator */
private SymbolList mSymbolList;
/** Whether it is displaying symbol list */
private boolean mSymbolMode;
/** Whether prediction is enabled */
private boolean mOptPrediction;
/** Whether spell correction is enabled */
private boolean mOptSpellCorrection;
/** Whether learning is enabled */
private boolean mOptLearning;
/** SHIFT key state */
private int mHardShift;
/** SHIFT key state (pressing) */
private boolean mShiftPressing;
/** ALT key state */
private int mHardAlt;
/** ALT key state (pressing) */
private boolean mAltPressing;
/** Instance of this service */
private static OpenWnnEN mSelf = null;
/** Shift lock toggle definition */
private static final int[] mShiftKeyToggle = {0, MetaKeyKeyListener.META_SHIFT_ON, MetaKeyKeyListener.META_CAP_LOCKED};
/** ALT lock toggle definition */
private static final int[] mAltKeyToggle = {0, MetaKeyKeyListener.META_ALT_ON, MetaKeyKeyListener.META_ALT_LOCKED};
/** Auto caps mode */
private boolean mAutoCaps = false;
/** Whether dismissing the keyboard when the enter key is pressed */
private boolean mEnableAutoHideKeyboard = true;
/** Tutorial */
private TutorialEN mTutorial;
/** Whether tutorial mode or not */
private boolean mEnableTutorial;
/** Message for {@code mHandler} (execute prediction) */
private static final int MSG_PREDICTION = 0;
/** Message for {@code mHandler} (execute tutorial) */
private static final int MSG_START_TUTORIAL = 1;
/** Message for {@code mHandler} (close) */
private static final int MSG_CLOSE = 2;
/** Delay time(msec.) to start prediction after key input when the candidates view is not shown. */
private static final int PREDICTION_DELAY_MS_1ST = 200;
/** Delay time(msec.) to start prediction after key input when the candidates view is shown. */
private static final int PREDICTION_DELAY_MS_SHOWING_CANDIDATE = 200;
/** {@code Handler} for drawing candidates/displaying tutorial */
Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_PREDICTION:
updatePrediction();
break;
case MSG_START_TUTORIAL:
if (mTutorial == null) {
if (isInputViewShown()) {
DefaultSoftKeyboardEN inputManager = ((DefaultSoftKeyboardEN) mInputViewManager);
View v = inputManager.getKeyboardView();
mTutorial = new TutorialEN(OpenWnnEN.this, v, inputManager);
mTutorial.start();
} else {
/* Try again soon if the view is not yet showing */
sendMessageDelayed(obtainMessage(MSG_START_TUTORIAL), 100);
}
}
break;
case MSG_CLOSE:
if (mConverterEN != null) mConverterEN.close();
if (mSymbolList != null) mSymbolList.close();
break;
}
}
};
/**
* Constructor
*/
public OpenWnnEN() {
super();
mSelf = this;
/* used by OpenWnn */
mComposingText = new ComposingText();
mCandidatesViewManager = new TextCandidatesViewManager(-1);
mInputViewManager = new DefaultSoftKeyboardEN();
if (OpenWnn.getCurrentIme() != null) {
if (mConverterEN == null) {
mConverterEN = new OpenWnnEngineEN("/data/data/jp.co.omronsoft.openwnn/writableEN.dic");
}
}
mConverter = mConverterEN;
mSymbolList = null;
/* etc */
mDisplayText = new SpannableStringBuilder();
mAutoHideMode = false;
mSymbolMode = false;
mOptPrediction = true;
mOptSpellCorrection = true;
mOptLearning = true;
}
/**
* Constructor
*
* @param context The context
*/
public OpenWnnEN(Context context) {
this();
attachBaseContext(context);
}
/**
* Get the instance of this service.
*
* Before using this method, the constructor of this service must be invoked.
*
* @return The instance of this object
*/
public static OpenWnnEN getInstance() {
return mSelf;
}
/**
* Insert a character into the composing text.
*
* @param chars A array of character
*/
private void insertCharToComposingText(char[] chars) {
StrSegment seg = new StrSegment(chars);
if (chars[0] == SPACE[0] || chars[0] == '\u0009') {
/* if the character is a space, commit the composing text */
commitText(1);
commitText(seg.string);
mComposingText.clear();
} else if (mWordSeparators.contains(seg.string)) {
/* if the character is a separator, remove an auto-inserted space and commit the composing text. */
if (mPreviousEventCode == OpenWnnEvent.SELECT_CANDIDATE) {
mInputConnection.deleteSurroundingText(1, 0);
}
commitText(1);
commitText(seg.string);
mComposingText.clear();
} else {
mComposingText.insertStrSegment(0, 1, seg);
updateComposingText(1);
}
}
/**
* Insert a character into the composing text.
*
* @param charCode A character code
* @return {@code true} if success; {@code false} if an error occurs.
*/
private boolean insertCharToComposingText(int charCode) {
if (charCode == 0) {
return false;
}
insertCharToComposingText(Character.toChars(charCode));
return true;
}
/**
* Get the shift key state from the editor.
*
* @param editor Editor
*
* @return State ID of the shift key (0:off, 1:on)
*/
protected int getShiftKeyState(EditorInfo editor) {
return (getCurrentInputConnection().getCursorCapsMode(editor.inputType) == 0) ? 0 : 1;
}
/**
* Set the mode of the symbol list.
*
* @param mode {@code SymbolList.SYMBOL_ENGLISH} or {@code null}.
*/
private void setSymbolMode(String mode) {
if (mode != null) {
mHandler.removeMessages(MSG_PREDICTION);
mSymbolMode = true;
mSymbolList.setDictionary(mode);
mConverter = mSymbolList;
} else {
if (!mSymbolMode) {
return;
}
mHandler.removeMessages(MSG_PREDICTION);
mSymbolMode = false;
mConverter = mConverterEN;
}
}
/***********************************************************************
* InputMethodServer
***********************************************************************/
/** @see jp.co.omronsoft.openwnn.OpenWnn#onCreate */
@Override public void onCreate() {
super.onCreate();
mWordSeparators = getResources().getString(R.string.en_word_separators);
if (mConverterEN == null) {
mConverterEN = new OpenWnnEngineEN("/data/data/jp.co.omronsoft.openwnn/writableEN.dic");
}
if (mSymbolList == null) {
mSymbolList = new SymbolList(this, SymbolList.LANG_EN);
}
}
/** @see jp.co.omronsoft.openwnn.OpenWnn#onCreateInputView */
@Override public View onCreateInputView() {
int hiddenState = getResources().getConfiguration().hardKeyboardHidden;
boolean hidden = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
((DefaultSoftKeyboardEN) mInputViewManager).setHardKeyboardHidden(hidden);
mEnableTutorial = hidden;
return super.onCreateInputView();
}
/** @see jp.co.omronsoft.openwnn.OpenWnn#onStartInputView */
@Override public void onStartInputView(EditorInfo attribute, boolean restarting) {
super.onStartInputView(attribute, restarting);
/* initialize views */
mCandidatesViewManager.clearCandidates();
mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE);
mHardShift = 0;
mHardAlt = 0;
updateMetaKeyStateDisplay();
/* load preferences */
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
/* auto caps mode */
mAutoCaps = pref.getBoolean("auto_caps", true);
/* set TextCandidatesViewManager's option */
((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(true);
/* display status icon */
showStatusIcon(R.drawable.immodeic_half_alphabet);
if (mComposingText != null) {
mComposingText.clear();
}
/* initialize the engine's state */
fitInputType(pref, attribute);
((DefaultSoftKeyboard) mInputViewManager).resetCurrentKeyboard();
if (OpenWnn.isXLarge()) {
mTextCandidatesViewManager.setPreferences(pref);
}
}
/** @see jp.co.omronsoft.openwnn.OpenWnn#hideWindow */
@Override public void hideWindow() {
((BaseInputView)((DefaultSoftKeyboard) mInputViewManager).getCurrentView()).closeDialog();
mComposingText.clear();
mInputViewManager.onUpdateState(this);
mHandler.removeMessages(MSG_START_TUTORIAL);
mInputViewManager.closing();
if (mTutorial != null) {
mTutorial.close();
mTutorial = null;
}
super.hideWindow();
}
/** @see jp.co.omronsoft.openwnn.OpenWnn#onUpdateSelection */
@Override public void onUpdateSelection(int oldSelStart, int oldSelEnd,
int newSelStart, int newSelEnd, int candidatesStart,
int candidatesEnd) {
boolean isNotComposing = ((candidatesStart < 0) && (candidatesEnd < 0));
if (isNotComposing) {
mComposingText.clear();
updateComposingText(1);
} else {
if (mComposingText.size(1) != 0) {
updateComposingText(1);
}
}
}
/** @see jp.co.omronsoft.openwnn.OpenWnn#onConfigurationChanged */
@Override public void onConfigurationChanged(Configuration newConfig) {
try {
super.onConfigurationChanged(newConfig);
if (mInputConnection != null) {
updateComposingText(1);
}
/* Hardware keyboard */
int hiddenState = newConfig.hardKeyboardHidden;
boolean hidden = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
mEnableTutorial = hidden;
} catch (Exception ex) {
}
}
/** @see jp.co.omronsoft.openwnn.OpenWnn#onEvaluateFullscreenMode */
@Override public boolean onEvaluateFullscreenMode() {
return false;
}
/** @see jp.co.omronsoft.openwnn.OpenWnn#onEvaluateInputViewShown */
@Override public boolean onEvaluateInputViewShown() {
return true;
}
/***********************************************************************
* OpenWnn
***********************************************************************/
/** @see jp.co.omronsoft.openwnn.OpenWnn#onEvent */
@Override synchronized public boolean onEvent(OpenWnnEvent ev) {
/* handling events which are valid when InputConnection is not active. */
switch (ev.code) {
case OpenWnnEvent.KEYUP:
onKeyUpEvent(ev.keyEvent);
return true;
case OpenWnnEvent.INITIALIZE_LEARNING_DICTIONARY:
return mConverterEN.initializeDictionary( WnnEngine.DICTIONARY_TYPE_LEARN );
case OpenWnnEvent.INITIALIZE_USER_DICTIONARY:
return mConverterEN.initializeDictionary( WnnEngine.DICTIONARY_TYPE_USER );
case OpenWnnEvent.LIST_WORDS_IN_USER_DICTIONARY:
mUserDictionaryWords = mConverterEN.getUserDictionaryWords( );
return true;
case OpenWnnEvent.GET_WORD:
if( mUserDictionaryWords != null ) {
ev.word = mUserDictionaryWords[ 0 ];
for( int i = 0 ; i < mUserDictionaryWords.length-1 ; i++ ) {
mUserDictionaryWords[ i ] = mUserDictionaryWords[ i + 1 ];
}
mUserDictionaryWords[ mUserDictionaryWords.length-1 ] = null;
if( mUserDictionaryWords[ 0 ] == null ) {
mUserDictionaryWords = null;
}
return true;
}
break;
case OpenWnnEvent.ADD_WORD:
mConverterEN.addWord(ev.word);
return true;
case OpenWnnEvent.DELETE_WORD:
mConverterEN.deleteWord(ev.word);
return true;
case OpenWnnEvent.CHANGE_MODE:
return false;
case OpenWnnEvent.UPDATE_CANDIDATE:
updateComposingText(ComposingText.LAYER1);
return true;
case OpenWnnEvent.CHANGE_INPUT_VIEW:
setInputView(onCreateInputView());
return true;
case OpenWnnEvent.CANDIDATE_VIEW_TOUCH:
boolean ret;
ret = ((TextCandidatesViewManager)mCandidatesViewManager).onTouchSync();
return ret;
default:
break;
}
dismissPopupKeyboard();
KeyEvent keyEvent = ev.keyEvent;
int keyCode = 0;
if (keyEvent != null) {
keyCode = keyEvent.getKeyCode();
}
if (mDirectInputMode) {
if (ev.code == OpenWnnEvent.INPUT_SOFT_KEY && mInputConnection != null) {
mInputConnection.sendKeyEvent(keyEvent);
mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
keyEvent.getKeyCode()));
}
return false;
}
if (ev.code == OpenWnnEvent.LIST_CANDIDATES_FULL) {
mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_FULL);
return true;
} else if (ev.code == OpenWnnEvent.LIST_CANDIDATES_NORMAL) {
mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
return true;
}
boolean ret = false;
switch (ev.code) {
case OpenWnnEvent.INPUT_CHAR:
((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(false);
EditorInfo edit = getCurrentInputEditorInfo();
if( edit.inputType == EditorInfo.TYPE_CLASS_PHONE){
commitText(new String(ev.chars));
}else{
setSymbolMode(null);
insertCharToComposingText(ev.chars);
ret = true;
mPreviousEventCode = ev.code;
}
break;
case OpenWnnEvent.INPUT_KEY:
keyCode = ev.keyEvent.getKeyCode();
/* update shift/alt state */
switch (keyCode) {
case KeyEvent.KEYCODE_ALT_LEFT:
case KeyEvent.KEYCODE_ALT_RIGHT:
if (ev.keyEvent.getRepeatCount() == 0) {
if (++mHardAlt > 2) { mHardAlt = 0; }
}
mAltPressing = true;
updateMetaKeyStateDisplay();
return true;
case KeyEvent.KEYCODE_SHIFT_LEFT:
case KeyEvent.KEYCODE_SHIFT_RIGHT:
if (ev.keyEvent.getRepeatCount() == 0) {
if (++mHardShift > 2) { mHardShift = 0; }
}
mShiftPressing = true;
updateMetaKeyStateDisplay();
return true;
}
setSymbolMode(null);
updateComposingText(1);
/* handle other key event */
ret = processKeyEvent(ev.keyEvent);
mPreviousEventCode = ev.code;
break;
case OpenWnnEvent.INPUT_SOFT_KEY:
setSymbolMode(null);
updateComposingText(1);
ret = processKeyEvent(ev.keyEvent);
if (!ret) {
int code = keyEvent.getKeyCode();
if (code == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER) {
sendKeyChar('\n');
} else {
mInputConnection.sendKeyEvent(keyEvent);
mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, code));
}
ret = true;
}
mPreviousEventCode = ev.code;
break;
case OpenWnnEvent.SELECT_CANDIDATE:
if (mSymbolMode) {
commitText(ev.word, false);
} else {
if (mWordSeparators.contains(ev.word.candidate) &&
mPreviousEventCode == OpenWnnEvent.SELECT_CANDIDATE) {
mInputConnection.deleteSurroundingText(1, 0);
}
commitText(ev.word, true);
}
mComposingText.clear();
mPreviousEventCode = ev.code;
updateComposingText(1);
break;
case OpenWnnEvent.LIST_SYMBOLS:
commitText(1);
mComposingText.clear();
setSymbolMode(SymbolList.SYMBOL_ENGLISH);
updateComposingText(1);
break;
default:
break;
}
if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
}
return ret;
}
/***********************************************************************
* OpenWnnEN
***********************************************************************/
/**
* Handling KeyEvent
*
* This method is called from {@link #onEvent()}.
*
* @param ev A key event
* @return {@code true} if the event is processed in this method; {@code false} if the event is not processed in this method
*/
private boolean processKeyEvent(KeyEvent ev) {
int key = ev.getKeyCode();
EditorInfo edit = getCurrentInputEditorInfo();
/* keys which produce a glyph */
if (ev.isPrintingKey()) {
/* do nothing if the character is not able to display or the character is dead key */
if ((mHardShift > 0 && mHardAlt > 0) || (ev.isAltPressed() && ev.isShiftPressed())) {
int charCode = ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON | MetaKeyKeyListener.META_ALT_ON);
if (charCode == 0 || (charCode & KeyCharacterMap.COMBINING_ACCENT) != 0 || charCode == PRIVATE_AREA_CODE) {
if(mHardShift == 1){
mShiftPressing = false;
}
if(mHardAlt == 1){
mAltPressing = false;
}
if(!ev.isAltPressed()){
if (mHardAlt == 1) {
mHardAlt = 0;
}
}
if(!ev.isShiftPressed()){
if (mHardShift == 1) {
mHardShift = 0;
}
}
if(!ev.isShiftPressed() && !ev.isAltPressed()){
updateMetaKeyStateDisplay();
}
return true;
}
}
((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(false);
/* get the key character */
if (mHardShift== 0 && mHardAlt == 0) {
/* no meta key is locked */
int shift = (mAutoCaps) ? getShiftKeyState(edit) : 0;
if (shift != mHardShift && (key >= KeyEvent.KEYCODE_A && key <= KeyEvent.KEYCODE_Z)) {
/* handling auto caps for a alphabet character */
insertCharToComposingText(ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON));
} else {
insertCharToComposingText(ev.getUnicodeChar());
}
} else {
insertCharToComposingText(ev.getUnicodeChar(mShiftKeyToggle[mHardShift]
| mAltKeyToggle[mHardAlt]));
if(mHardShift == 1){
mShiftPressing = false;
}
if(mHardAlt == 1){
mAltPressing = false;
}
/* back to 0 (off) if 1 (on/not locked) */
if(!ev.isAltPressed()){
if (mHardAlt == 1) {
mHardAlt = 0;
}
}
if(!ev.isShiftPressed()){
if (mHardShift == 1) {
mHardShift = 0;
}
}
if(!ev.isShiftPressed() && !ev.isAltPressed()){
updateMetaKeyStateDisplay();
}
}
if (edit.inputType == EditorInfo.TYPE_CLASS_PHONE) {
commitText(1);
mComposingText.clear();
return true;
}
return true;
} else if (key == KeyEvent.KEYCODE_SPACE) {
if (ev.isAltPressed()) {
/* display the symbol list (G1 specific. same as KEYCODE_SYM) */
commitText(1);
mComposingText.clear();
setSymbolMode(SymbolList.SYMBOL_ENGLISH);
updateComposingText(1);
mHardAlt = 0;
updateMetaKeyStateDisplay();
} else {
insertCharToComposingText(SPACE);
}
return true;
} else if (key == KeyEvent.KEYCODE_SYM) {
/* display the symbol list */
commitText(1);
mComposingText.clear();
setSymbolMode(SymbolList.SYMBOL_ENGLISH);
updateComposingText(1);
mHardAlt = 0;
updateMetaKeyStateDisplay();
}
/* Functional key */
if (mComposingText.size(1) > 0) {
switch (key) {
case KeyEvent.KEYCODE_DEL:
mComposingText.delete(1, false);
updateComposingText(1);
return true;
case KeyEvent.KEYCODE_BACK:
if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
} else {
mComposingText.clear();
updateComposingText(1);
}
return true;
case KeyEvent.KEYCODE_DPAD_LEFT:
mComposingText.moveCursor(1, -1);
updateComposingText(1);
return true;
case KeyEvent.KEYCODE_DPAD_RIGHT:
mComposingText.moveCursor(1, 1);
updateComposingText(1);
return true;
case KeyEvent.KEYCODE_ENTER:
case KeyEvent.KEYCODE_NUMPAD_ENTER:
case KeyEvent.KEYCODE_DPAD_CENTER:
commitText(1);
mComposingText.clear();
if (mEnableAutoHideKeyboard) {
mInputViewManager.closing();
requestHideSelf(0);
}
return true;
default:
return !isThroughKeyCode(key);
}
} else {
/* if there is no composing string. */
if (mCandidatesViewManager.getCurrentView().isShown()) {
if (key == KeyEvent.KEYCODE_BACK) {
if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
} else {
mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE);
}
return true;
}
} else {
switch (key) {
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
case KeyEvent.KEYCODE_NUMPAD_ENTER:
if (mEnableAutoHideKeyboard) {
mInputViewManager.closing();
requestHideSelf(0);
return true;
}
break;
case KeyEvent.KEYCODE_BACK:
/*
* If 'BACK' key is pressed when the SW-keyboard is shown
* and the candidates view is not shown, dismiss the SW-keyboard.
*/
if (isInputViewShown()) {
mInputViewManager.closing();
requestHideSelf(0);
return true;
}
break;
default:
break;
}
}
}
return false;
}
/**
* Thread for updating the candidates view
*/
private void updatePrediction() {
int candidates = 0;
if (mConverter != null) {
/* normal prediction */
candidates = mConverter.predict(mComposingText, 0, -1);
}
/* update the candidates view */
if (candidates > 0) {
mCandidatesViewManager.displayCandidates(mConverter);
} else {
mCandidatesViewManager.clearCandidates();
}
}
/**
* Update the composing text.
*
* @param layer {@link mComposingText}'s layer to display
*/
private void updateComposingText(int layer) {
/* update the candidates view */
if (!mOptPrediction) {
commitText(1);
mComposingText.clear();
if (mSymbolMode) {
mHandler.removeMessages(MSG_PREDICTION);
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION), 0);
}
} else {
if (mComposingText.size(1) != 0) {
mHandler.removeMessages(MSG_PREDICTION);
if (mCandidatesViewManager.getCurrentView().isShown()) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION),
PREDICTION_DELAY_MS_SHOWING_CANDIDATE);
} else {
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION),
PREDICTION_DELAY_MS_1ST);
}
} else {
mHandler.removeMessages(MSG_PREDICTION);
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION), 0);
}
/* notice to the input view */
this.mInputViewManager.onUpdateState(this);
/* set the text for displaying as the composing text */
SpannableStringBuilder disp = mDisplayText;
disp.clear();
disp.insert(0, mComposingText.toString(layer));
/* add decoration to the text */
int cursor = mComposingText.getCursor(layer);
if (disp.length() != 0) {
if (cursor > 0 && cursor < disp.length()) {
disp.setSpan(SPAN_EXACT_BGCOLOR_HL, 0, cursor,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (cursor < disp.length()) {
mDisplayText.setSpan(SPAN_REMAIN_BGCOLOR_HL, cursor, disp.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mDisplayText.setSpan(SPAN_TEXTCOLOR, 0, disp.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
disp.setSpan(SPAN_UNDERLINE, 0, disp.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
int displayCursor = cursor;
if (FIX_CURSOR_TEXT_END) {
displayCursor = (cursor == 0) ? 0 : 1;
}
/* update the composing text on the EditView */
mInputConnection.setComposingText(disp, displayCursor);
}
}
/**
* Commit the composing text.
*
* @param layer {@link mComposingText}'s layer to commit.
*/
private void commitText(int layer) {
String tmp = mComposingText.toString(layer);
if (mOptLearning && mConverter != null && tmp.length() > 0) {
WnnWord word = new WnnWord(tmp, tmp);
mConverter.learn(word);
}
mInputConnection.commitText(tmp, (FIX_CURSOR_TEXT_END ? 1 : tmp.length()));
mCandidatesViewManager.clearCandidates();
}
/**
* Commit a word
*
* @param word A word to commit
* @param withSpace Append a space after the word if {@code true}.
*/
private void commitText(WnnWord word, boolean withSpace) {
if (mOptLearning && mConverter != null) {
mConverter.learn(word);
}
mInputConnection.commitText(word.candidate, (FIX_CURSOR_TEXT_END ? 1 : word.candidate.length()));
if (withSpace) {
commitText(" ");
}
}
/**
* Commit a string
*
* The string is not registered into the learning dictionary.
*
* @param str A string to commit
*/
private void commitText(String str) {
mInputConnection.commitText(str, (FIX_CURSOR_TEXT_END ? 1 : str.length()));
mCandidatesViewManager.clearCandidates();
}
/**
* Dismiss the pop-up keyboard
*/
protected void dismissPopupKeyboard() {
DefaultSoftKeyboardEN kbd = (DefaultSoftKeyboardEN)mInputViewManager;
if (kbd != null) {
kbd.dismissPopupKeyboard();
}
}
/**
* Display current meta-key state.
*/
private void updateMetaKeyStateDisplay() {
int mode = 0;
if(mHardShift == 0 && mHardAlt == 0){
mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF;
}else if(mHardShift == 1 && mHardAlt == 0){
mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_OFF;
}else if(mHardShift == 2 && mHardAlt == 0){
mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_OFF;
}else if(mHardShift == 0 && mHardAlt == 1){
mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_ON;
}else if(mHardShift == 0 && mHardAlt == 2){
mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_LOCK;
}else if(mHardShift == 1 && mHardAlt == 1){
mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_ON;
}else if(mHardShift == 1 && mHardAlt == 2){
mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_LOCK;
}else if(mHardShift == 2 && mHardAlt == 1){
mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_ON;
}else if(mHardShift == 2 && mHardAlt == 2){
mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_LOCK;
}else{
mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF;
}
((DefaultSoftKeyboard) mInputViewManager).updateIndicator(mode);
}
/**
* Handling KeyEvent(KEYUP)
*
* This method is called from {@link #onEvent()}.
*
* @param ev An up key event
*/
private void onKeyUpEvent(KeyEvent ev) {
int key = ev.getKeyCode();
if(!mShiftPressing){
if(key == KeyEvent.KEYCODE_SHIFT_LEFT || key == KeyEvent.KEYCODE_SHIFT_RIGHT){
mHardShift = 0;
mShiftPressing = true;
updateMetaKeyStateDisplay();
}
}
if(!mAltPressing ){
if(key == KeyEvent.KEYCODE_ALT_LEFT || key == KeyEvent.KEYCODE_ALT_RIGHT){
mHardAlt = 0;
mAltPressing = true;
updateMetaKeyStateDisplay();
}
}
}
/**
* Fits an editor info.
*
* @param preferences The preference data.
* @param info The editor info.
*/
private void fitInputType(SharedPreferences preference, EditorInfo info) {
if (info.inputType == EditorInfo.TYPE_NULL) {
mDirectInputMode = true;
return;
}
mEnableAutoHideKeyboard = false;
/* set prediction & spell correction mode */
mOptPrediction = preference.getBoolean("opt_en_prediction", true);
mOptSpellCorrection = preference.getBoolean("opt_en_spell_correction", true);
mOptLearning = preference.getBoolean("opt_en_enable_learning", true);
/* prediction on/off */
switch (info.inputType & EditorInfo.TYPE_MASK_CLASS) {
case EditorInfo.TYPE_CLASS_NUMBER:
case EditorInfo.TYPE_CLASS_DATETIME:
case EditorInfo.TYPE_CLASS_PHONE:
mOptPrediction = false;
mOptLearning = false;
break;
case EditorInfo.TYPE_CLASS_TEXT:
switch (info.inputType & EditorInfo.TYPE_MASK_VARIATION) {
case EditorInfo.TYPE_TEXT_VARIATION_PASSWORD:
case EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD:
mOptLearning = false;
mOptPrediction = false;
break;
case EditorInfo.TYPE_TEXT_VARIATION_PHONETIC:
mOptLearning = false;
mOptPrediction = false;
break;
default:
break;
}
}
/* doesn't learn any word if it is not prediction mode */
if (!mOptPrediction) {
mOptLearning = false;
}
/* set engine's mode */
if (mOptSpellCorrection) {
mConverterEN.setDictionary(OpenWnnEngineEN.DICT_FOR_CORRECT_MISTYPE);
} else {
mConverterEN.setDictionary(OpenWnnEngineEN.DICT_DEFAULT);
}
checkTutorial(info.privateImeOptions);
}
/**
* Check and start the tutorial if it is the tutorial mode.
*
* @param privateImeOptions IME's options
*/
private void checkTutorial(String privateImeOptions) {
if (privateImeOptions == null) return;
if (privateImeOptions.equals("com.google.android.setupwizard:ShowTutorial")) {
if ((mTutorial == null) && mEnableTutorial) startTutorial();
} else if (privateImeOptions.equals("com.google.android.setupwizard:HideTutorial")) {
if (mTutorial != null) {
if (mTutorial.close()) {
mTutorial = null;
}
}
}
}
/**
* Start the tutorial
*/
private void startTutorial() {
DefaultSoftKeyboardEN inputManager = ((DefaultSoftKeyboardEN) mInputViewManager);
View v = inputManager.getKeyboardView();
v.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return true;
}});
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_TUTORIAL), 500);
}
/**
* Close the tutorial
*/
public void tutorialDone() {
mTutorial = null;
}
/** @see OpenWnn#close */
@Override protected void close() {
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLOSE), 0);
}
}