/*
 * ◇
 * Copyright (C) 2008,2009  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 net.gorry.android.input.nicownng;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.gorry.android.input.nicownng.EN.NicoWnnGEngineEN;
import net.gorry.android.input.nicownng.JAJP.DefaultSoftKeyboardJAJP;
import net.gorry.android.input.nicownng.JAJP.DefaultSoftKeyboardNico;
import net.gorry.android.input.nicownng.JAJP.DefaultSoftKeyboardNico2;
import net.gorry.android.input.nicownng.JAJP.KanaConverter;
import net.gorry.android.input.nicownng.JAJP.NicoWnnGEngineJAJP;
import net.gorry.android.input.nicownng.JAJP.Romkan;
import net.gorry.android.input.nicownng.JAJP.RomkanFullKatakana;
import net.gorry.android.input.nicownng.JAJP.RomkanHalfKatakana;
import net.gorry.android.input.nicownng.JAJP.SetupKeyboard2Touch;
import net.gorry.android.input.nicownng.JAJP.SetupKeyboardBell;
import net.gorry.android.input.nicownng.JAJP.SetupKeyboardNico;
import net.gorry.android.input.nicownng.JAJP.SetupKeyboardNico2;
import net.gorry.android.input.nicownng.JAJP.TutorialJAJP;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.text.InputType;
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;
import android.view.inputmethod.InputConnection;

/**
 * The OpenWnn Japanese IME class
 *
 * @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD.  All Rights Reserved.
 */
public class NicoWnnGJAJP extends NicoWnnG {
	/**
	 * Mode of the convert engine (Full-width KATAKANA).
	 * Use with {@code OpenWnn.CHANGE_MODE} event.
	 */
	public static final int ENGINE_MODE_FULL_KATAKANA = 101;

	/**
	 * Mode of the convert engine (Half-width KATAKANA).
	 * Use with {@code OpenWnn.CHANGE_MODE} event.
	 */
	public static final int ENGINE_MODE_HALF_KATAKANA = 102;

	/**
	 * Mode of the convert engine (EISU-KANA conversion).
	 * Use with {@code OpenWnn.CHANGE_MODE} event.
	 */
	public static final int ENGINE_MODE_EISU_KANA = 103;

	/**
	 * Mode of the convert engine (Symbol list).
	 * Use with {@code OpenWnn.CHANGE_MODE} event.
	 */
	public static final int ENGINE_MODE_SYMBOL = 104;
	public static final int ENGINE_MODE_DOCOMOSYMBOL = 107;

	/**
	 * Mode of the convert engine (Keyboard type is QWERTY).
	 * Use with {@code OpenWnn.CHANGE_MODE} event to change ambiguous searching pattern.
	 */
	public static final int ENGINE_MODE_OPT_TYPE_QWERTY = 105;

	/**
	 * Mode of the convert engine (Keyboard type is 12-keys).
	 * Use with {@code OpenWnn.CHANGE_MODE} event to change ambiguous searching pattern.
	 */
	public static final int ENGINE_MODE_OPT_TYPE_12KEY = 106;

	/**
	 * Mode of the convert engine (Full-width KATAKANA).
	 * Use with {@code OpenWnn.CHANGE_MODE} event.
	 */
	public static final int ENGINE_MODE_FULL_KATAKANA_CONV = 108;

	/** Never move cursor in to the composing text (adapting to IMF's specification change) */
	private static final boolean FIX_CURSOR_TEXT_END = true;

	/** Highlight color style for the converted clause */
	private static final CharacterStyle SPAN_CONVERT_BGCOLOR_HL   = new BackgroundColorSpan(0xFF8888FF);
	/** Highlight color style for the selected string  */
	private static final CharacterStyle SPAN_EXACT_BGCOLOR_HL     = new BackgroundColorSpan(0xFF66CDAA);
	/** Highlight color style for EISU-KANA conversion */
	private static final CharacterStyle SPAN_EISUKANA_BGCOLOR_HL  = new BackgroundColorSpan(0xFF9FB6CD);
	/** 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);
	/** Underline style for the composing text */
	private static final CharacterStyle SPAN_UNDERLINE            = new UnderlineSpan();

	/** IME's status for {@code mStatus} input/no candidates). */
	private static final int STATUS_INIT            = 0x0000;
	/** IME's status for {@code mStatus}(input characters). */
	private static final int STATUS_INPUT           = 0x0001;
	/** IME's status for {@code mStatus}(input functional keys). */
	private static final int STATUS_INPUT_EDIT      = 0x0003;
	/** IME's status for {@code mStatus}(all candidates are displayed). */
	private static final int STATUS_CANDIDATE_FULL  = 0x0010;

	/** Alphabet-last pattern */
	private static final Pattern ENGLISH_CHARACTER_LAST = Pattern.compile(".*[a-zA-Z]$");

	/**
	 *  Private area character code got by {@link KeyEvent#getUnicodeChar()}.
	 *   (SHIFT+ALT+X G1 specific)
	 */
	private static final int PRIVATE_AREA_CODE = 61184;

	/** Maximum length of input string */
	private static final int LIMIT_INPUT_NUMBER = 30;

	/** Bit flag for English auto commit mode (ON) */
	private static final int AUTO_COMMIT_ENGLISH_ON      = 0x0000;
	/** Bit flag for English auto commit mode (OFF) */
	private static final int AUTO_COMMIT_ENGLISH_OFF     = 0x0001;
	/** Bit flag for English auto commit mode (symbol list) */
	private static final int AUTO_COMMIT_ENGLISH_SYMBOL  = 0x0010;

	/** 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 = 400;
	private static final int PREDICTION_DELAY_MS_1ST_00 = 250;
	private static final int PREDICTION_DELAY_MS_1ST_01 = 150;

	/** 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;
	private static final int PREDICTION_DELAY_MS_SHOWING_CANDIDATE_00 = 150;
	private static final int PREDICTION_DELAY_MS_SHOWING_CANDIDATE_01 = 150;


	private int mPredictionDelayMS1st = PREDICTION_DELAY_MS_1ST_00;
	private int mPredictionDelayMSShowingCandidate = PREDICTION_DELAY_MS_1ST_01;

	/** Convert engine's state */
	private class EngineState {
		/** Definition for {@code EngineState.*} (invalid) */
		public static final int INVALID = -1;

		/** Definition for {@code EngineState.dictionarySet} (Japanese) */
		public static final int DICTIONARYSET_JP = 0;

		/** Definition for {@code EngineState.dictionarySet} (English) */
		public static final int DICTIONARYSET_EN = 1;

		/** Definition for {@code EngineState.convertType} (prediction/no conversion) */
		public static final int CONVERT_TYPE_NONE = 0;

		/** Definition for {@code EngineState.convertType} (consecutive clause conversion) */
		public static final int CONVERT_TYPE_RENBUN = 1;

		/** Definition for {@code EngineState.convertType} (EISU-KANA conversion) */
		public static final int CONVERT_TYPE_EISU_KANA = 2;

		/** Definition for {@code EngineState.convertType} (KANA Direct conversion) */
		public static final int CONVERT_TYPE_KANA_DIRECT = 3;

		/** Definition for {@code EngineState.convertType} (consecutive clause predict conversion) */
		public static final int CONVERT_TYPE_PREDICT = 4;

		/** Definition for {@code EngineState.temporaryMode} (change back to the normal dictionary) */
		public static final int TEMPORARY_DICTIONARY_MODE_NONE = 0;

		/** Definition for {@code EngineState.temporaryMode} (change to the symbol dictionary) */
		public static final int TEMPORARY_DICTIONARY_MODE_SYMBOL = 1;
		public static final int TEMPORARY_DICTIONARY_MODE_DOCOMOSYMBOL00 = 3;
		public static final int TEMPORARY_DICTIONARY_MODE_DOCOMOSYMBOL01 = 4;

		/** Definition for {@code EngineState.temporaryMode} (change to the user dictionary) */
		public static final int TEMPORARY_DICTIONARY_MODE_USER = 2;

		/** Definition for {@code EngineState.preferenceDictionary} (no preference dictionary) */
		public static final int PREFERENCE_DICTIONARY_NONE = 0;

		/** Definition for {@code EngineState.preferenceDictionary} (person's name) */
		public static final int PREFERENCE_DICTIONARY_PERSON_NAME = 1;

		/** Definition for {@code EngineState.preferenceDictionary} (place name) */
		public static final int PREFERENCE_DICTIONARY_POSTAL_ADDRESS = 2;

		/** Definition for {@code EngineState.preferenceDictionary} (email/URI) */
		public static final int PREFERENCE_DICTIONARY_EMAIL_ADDRESS_URI = 3;

		/** Definition for {@code EngineState.keyboard} (undefined) */
		public static final int KEYBOARD_UNDEF = 0;

		/** Definition for {@code EngineState.keyboard} (QWERTY) */
		public static final int KEYBOARD_QWERTY = 1;

		/** Definition for {@code EngineState.keyboard} (12-keys) */
		public static final int KEYBOARD_12KEY  = 2;

		/** Set of dictionaries */
		public int dictionarySet = INVALID;

		/** Type of conversion */
		public int convertType = INVALID;

		/** Temporary mode */
		public int temporaryMode = INVALID;

		/** Preference dictionary setting */
		public int preferenceDictionary = INVALID;

		/** keyboard */
		public int keyboard = INVALID;

		/**
		 * Returns whether current type of conversion is consecutive clause(RENBUNSETSU) conversion.
		 *
		 * @return {@code true} if current type of conversion is consecutive clause conversion.
		 */
		public boolean isRenbun() {
			return convertType == CONVERT_TYPE_RENBUN;
		}

		/**
		 * Returns whether current type of conversion is consecutive clause(PREDICT) conversion.
		 *
		 * @return {@code true} if current type of conversion is consecutive clause conversion.
		 */
		public boolean isPredict() {
			return convertType == CONVERT_TYPE_PREDICT;
		}

		/**
		 * Returns whether current type of conversion is EISU-KANA conversion.
		 *
		 * @return {@code true} if current type of conversion is EISU-KANA conversion.
		 */
		public boolean isEisuKana() {
			return convertType == CONVERT_TYPE_EISU_KANA;
		}

		/**
		 * Returns whether current type of conversion is KANA Direct conversion.
		 *
		 * @return {@code true} if current type of conversion is KANA Direct conversion.
		 */
		public boolean isKanaDirect() {
			return convertType == CONVERT_TYPE_KANA_DIRECT;
		}

		/**
		 * Returns whether current type of conversion is no conversion.
		 *
		 * @return {@code true} if no conversion is executed currently.
		 */
		public boolean isConvertState() {
			return convertType != CONVERT_TYPE_NONE;
		}

		/**
		 * Check whether or not the mode is "symbol list".
		 *
		 * @return {@code true} if the mode is "symbol list".
		 */
		public boolean isSymbolList() {
			return (temporaryMode == TEMPORARY_DICTIONARY_MODE_SYMBOL);
			//return ((temporaryMode == TEMPORARY_DICTIONARY_MODE_SYMBOL) || (temporaryMode == TEMPORARY_DICTIONARY_MODE_DOCOMOSYMBOL));
		}
		public boolean isDocomoSymbolList() {
			return ((temporaryMode == TEMPORARY_DICTIONARY_MODE_DOCOMOSYMBOL00) || (temporaryMode == TEMPORARY_DICTIONARY_MODE_DOCOMOSYMBOL01));
		}

		/**
		 * Check whether or not the current language is English.
		 *
		 * @return {@code true} if the current language is English.
		 */
		public boolean isEnglish() {
			return dictionarySet == DICTIONARYSET_EN;
		}
	}

	/** IME's status */
	protected int mStatus = STATUS_INIT;

	/** Whether exact match searching or not */
	protected boolean mExactMatchMode = false;

	/** Spannable string builder for displaying the composing text */
	protected SpannableStringBuilder mDisplayText;

	/** Instance of this service */
	private static NicoWnnGJAJP mSelf = null;

	/** Backup for switching the converter */
	private WnnEngine mConverterBack;

	/** Backup for switching the pre-converter */
	private LetterConverter mPreConverterBack;

	/** OpenWnn conversion engine for Japanese */
	private NicoWnnGEngineJAJP mConverterJAJP;

	/** OpenWnn conversion engine for English */
	private final NicoWnnGEngineEN mConverterEN;

	/** Conversion engine for listing symbols */
	private SymbolList mConverterSymbolEngineBack;

	/** Symbol lists to display when the symbol key is pressed */
	/*
	private static final String[] SYMBOL_LISTS = {
		SymbolList.SYMBOL_JAPANESE, SymbolList.SYMBOL_ENGLISH, SymbolList.SYMBOL_JAPANESE_FACE,  SymbolList.SYMBOL_JAPANESE_USER_FACE
	};
	 */

	/** Current symbol list */
	private int mCurrentSymbol = -1;

	/** is change normal/full */
	private int mDocomoEmojiCount = 0;

	/** prediction count **/
	private int mPredictionCount = 0;
	private int mPredictionMode  = 0;
	private boolean mEnablePredictionAfterEnter;


	private static final int kPREDICTION_ON     = 0;
	private static final int kPREDICTION_OFF    = 1;
	private static final int kPREDICTION_HENKAN = 2;
	private static final HashMap<String, Integer> predictionModeTable = new HashMap<String, Integer>() {
		private static final long serialVersionUID = 1L;
		{
			put("on_prediction", 0);
			put("off_prediction", 1);
			put("henkan_prediction", 2);
		}
	};


	/** Romaji-to-Kana converter (HIRAGANA) */
	private Romkan mPreConverterHiragana;

	/** Romaji-to-Kana converter (full-width KATAKANA) */
	private final RomkanFullKatakana mPreConverterFullKatakana;

	/** Romaji-to-Kana converter (half-width KATAKANA) */
	private final RomkanHalfKatakana mPreConverterHalfKatakana;

	/** Conversion Engine's state */
	private final EngineState mEngineState = new EngineState();

	/** Whether learning function is active of not. */
	private boolean mEnableLearning = true;

	/** Whether prediction is active or not. */
	//private boolean mEnablePrediction = true;
	private boolean mActionPrediction = true;

	/** Whether using the converter */
	private boolean mEnableConverter = true;

	/** Whether displaying the symbol list */
	private boolean mEnableSymbolList = true;

	/** Whether non ASCII code is enabled */
	private boolean mEnableSymbolListNonHalf = true;

	/** Enable mistyping spell correction or not */
	private boolean mEnableSpellCorrection = true;

	/** Auto commit state (in English mode) */
	private int mDisableAutoCommitEnglishMask = AUTO_COMMIT_ENGLISH_ON;

	/** Whether removing a space before a separator or not. (in English mode) */
	private boolean mEnableAutoDeleteSpace = false;

	/** Whether auto-spacing is enabled or not. */
	private boolean mEnableAutoInsertSpace = true;

	/** Whether dismissing the keyboard when the enter key is pressed */
	private boolean mEnableAutoHideKeyboard = true;

	/** Number of committed clauses on consecutive clause conversion */
	private int mCommitCount = 0;

	/** Target layer of the {@link ComposingText} */
	private int mTargetLayer = 1;

	/** Current orientation of the display */
	private int mOrientation = Configuration.ORIENTATION_UNDEFINED;

	/** Current normal dictionary set */
	private int mPrevDictionarySet = NicoWnnGEngineJAJP.DIC_LANG_INIT;

	/** Regular expression pattern for English separators */
	private  Pattern mEnglishAutoCommitDelimiter = null;

	/** Cursor position in the composing text */
	private int mComposingStartCursor = 0;

	/** Cursor position before committing text */
	private int mCommitStartCursor = 0;

	/** Previous committed text */
	private StringBuffer mPrevCommitText = null;

	/** Call count of {@code commitText} */
	private int mPrevCommitCount = 0;

	/** Shift lock status of the Hardware keyboard */
	private int mHardShift;

	/** SHIFT key state (pressing) */
	private boolean mShiftPressing;

	/** ALT lock status of the Hardware keyboard */
	private int mHardAlt;

	/** ALT key state (pressing) */
	private boolean mAltPressing;

	/** 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;

	/** List of words in the user dictionary */
	private WnnWord[] mUserDictionaryWords = null;

	/** Tutorial */
	private TutorialJAJP mTutorial;

	/** Whether tutorial mode or not */
	private boolean mEnableTutorial;

	/** Whether there is a continued predicted candidate */
	private boolean mHasContinuedPrediction = false;

	/* recreate inputview mode */
	private String mNewInputViewMode;

	/** 候補選択に左右矢印キーを使う */
	private boolean mUseLeftRightKeyCandidateSelection = true;

	/* IS01 Hard key enabler */
	private final boolean mIs01Enable = true;
	private final boolean mIs12keyEnable = true;
	private NicoWnnGEvent evHardwareKeyboardIS01MojiKey;
	private final int mTimeOutLongPressHardwareKeyboardIS01MojiKey = 1000;
	private boolean mRegisterLongPressHardwareKeyboardIS01MojiKey = false;
	private final Handler mHandlerLongPressHardwareKeyboardIS01MojiKey = new Handler();
	private final Runnable mActionLongPressHardwareKeyboardIS01MojiKey = new Runnable() {
		public void run() {
			synchronized (this) {
				if (mRegisterLongPressHardwareKeyboardIS01MojiKey) {
					mHandlerLongPressHardwareKeyboardIS01MojiKey.removeCallbacks(mActionLongPressHardwareKeyboardIS01MojiKey);
					mRegisterLongPressHardwareKeyboardIS01MojiKey = false;
					openPreferenceSetting();
				}
			}
		}
	};

	private NicoWnnGEvent evHardwareKeyboardIS01EKaoKiKey;
	private boolean mRegisterLongPressHardwareKeyboardIS01EKaoKiKey = false;
	private final int mTimeOutLongPressHardwareKeyboardIS01EKaoKiKey = 500;
	private final Handler mHandlerLongPressHardwareKeyboardIS01EKaoKiKey = new Handler();
	private final Runnable mActionLongPressHardwareKeyboardIS01EKaoKiKey = new Runnable() {
		public void run() {
			synchronized (this) {
				if (mRegisterLongPressHardwareKeyboardIS01EKaoKiKey) {
					mHandlerLongPressHardwareKeyboardIS01EKaoKiKey.removeCallbacks(mActionLongPressHardwareKeyboardIS01EKaoKiKey);
					mRegisterLongPressHardwareKeyboardIS01EKaoKiKey = false;
					if (mSelf != null) {
						final NicoWnnGEvent mEventInputBack        = new NicoWnnGEvent(NicoWnnGEvent.INPUT_KEY, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
						final String str = getComposingText(ComposingText.LAYER2);
						onEvent(mEventInputBack);
						invokeMushroom(str);
					}
				}
			}
		}
	};

	private boolean mInputOrientation = false;

	/** {@code Handler} for drawing candidates/displaying tutorial */
	Handler mHandler = new Handler() {
		@Override
		public void handleMessage(final Message msg) {
			switch (msg.what) {
				case MSG_PREDICTION:
					updatePrediction();
					break;
				case MSG_START_TUTORIAL:
					if (mTutorial == null) {
						if (isInputViewShown()) {
							final DefaultSoftKeyboardJAJP inputManager = ((DefaultSoftKeyboardJAJP) mInputViewManager);
							final View v = inputManager.getKeyboardView();
							mTutorial = new TutorialJAJP(NicoWnnGJAJP.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 (mConverterJAJP != null) mConverterJAJP.close();
					if (mConverterEN != null) mConverterEN.close();
					if (mConverterSymbolEngineBack != null) mConverterSymbolEngineBack.close();
					break;
			}
		}
	};

	/** The candidate filter */
	private final CandidateFilter mFilter;

	/**
	 * Constructor
	 */
	public NicoWnnGJAJP() {
		super();
		mSelf = this;
		mComposingText = new ComposingText();
		mCandidatesViewManager = new TextCandidatesViewManager(-1);
		startSoftKeyboard();
		mConverter = mConverterJAJP = new NicoWnnGEngineJAJP(NicoWnnG.writableJAJPDic);
		mConverterEN = new NicoWnnGEngineEN(NicoWnnG.writableENDic);
		mPreConverter = mPreConverterHiragana = new Romkan();
		mPreConverterFullKatakana = new RomkanFullKatakana();
		mPreConverterHalfKatakana = new RomkanHalfKatakana();
		mFilter = new CandidateFilter();

		mDisplayText = new SpannableStringBuilder();
		mAutoHideMode = false;

		mPrevCommitText = new StringBuffer();
	}

	/**
	 * Constructor
	 *
	 * @param context       The context
	 */
	public NicoWnnGJAJP(final Context context) {
		this();
		attachBaseContext(context);
	}

	/** @see net.gorry.android.input.nicownng.NicoWnnG#onCreate */
	@Override public void onCreate() {
		super.onCreate();

		final String delimiter = Pattern.quote(getResources().getString(R.string.en_word_separators));
		mEnglishAutoCommitDelimiter = Pattern.compile(".*[" + delimiter + "]$");
		if (mConverterSymbolEngineBack == null) {
			mConverterSymbolEngineBack = new SymbolList(this, SymbolList.LANG_JA);
		}
	}

	/** @see net.gorry.android.input.nicownng.NicoWnnG#onCreateInputView */
	@Override public View onCreateInputView() {
		final SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
		mInputViewFullScreenInLandscape = pref.getBoolean("fullscreen", false);
		final String newinputmode = getOrientPrefString(pref, "input_mode", INPUTMODE_NORMAL);
		boolean start = false;
		if (false == mInputViewMode.equals(newinputmode)) {
			mInputViewMode = newinputmode;
			start = true;
		}
		if (mInputOrientation != super.getOrientPrefKeyMode()) {
			mInputOrientation = super.getOrientPrefKeyMode();
			start = true;
		}
		if (false == mInputViewMode.equals(newinputmode)) {
			mInputViewMode = newinputmode;
			start = true;
		}
		if (start) {
			startSoftKeyboard();
		}
		final int hiddenState = getResources().getConfiguration().hardKeyboardHidden;
		final boolean hidden  = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
		mEnableTutorial = hidden;
		return super.onCreateInputView();
	}

	/** @see net.gorry.android.input.nicownng.NicoWnnG#onStartInputView */
	@Override public void onStartInputView(final EditorInfo attribute, final boolean restarting) {
		final SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);

		boolean isrestart = false;
		final String newinputmode = getOrientPrefString(pref, "input_mode", INPUTMODE_NORMAL);
		if (false == mInputViewMode.equals(newinputmode)) {
			isrestart = true;
			mInputViewMode = newinputmode;
		}
		/* get old state */
		final boolean oldstate = ((DefaultSoftKeyboard)mInputViewManager).isHideSoftKeyboardByHardKeyboard();
		/* setup new hardkeyboard state */
		final int hardkeyState = getResources().getConfiguration().keyboard;
		final boolean hardkey  = (hardkeyState >= Configuration.KEYBOARD_QWERTY);
		final int hiddenState = getResources().getConfiguration().hardKeyboardHidden;
		final boolean hidden  = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
		if (null != mInputViewManager) {
			((DefaultSoftKeyboard)mInputViewManager).setHardKeyboardHidden(hidden, hardkey);
			/* get new state */
			{
				final boolean newstate = ((DefaultSoftKeyboard)mInputViewManager).isHideSoftKeyboardByHardKeyboard();
				if (oldstate != newstate) {
					isrestart = true;
				}
			}
			/* check softkeyboard hidden */
			{
				final int i = Integer.valueOf(getOrientPrefString(pref, "hidden_softkeyboard2", "0"));
				if (((DefaultSoftKeyboard)mInputViewManager).getHiddenSoftKeyboard() != i) {
					isrestart = true;
				}
			}
			/* check inputview height */
			{
				final int getindex = Integer.valueOf(getOrientPrefString(pref, "mainview_height_mode2", "0"));
				if (((DefaultSoftKeyboard)mInputViewManager).mInputViewHeightIndex != getindex) {
					isrestart = true;
				}
			}
			/* check qwerty kana mode */
			{
				final int qwertykanamode = Integer.valueOf(getOrientPrefString(pref, "qwerty_kana_mode", "0"));
				if (((DefaultSoftKeyboard)mInputViewManager).mQwertyKanaMode != qwertykanamode) {
					isrestart = true;
				}
			}
			/* check qwerty slant mode */
			{
				final boolean qwertymatrixmode = getOrientPrefBoolean(pref, "qwerty_matrix_mode", false);
				if (((DefaultSoftKeyboard)mInputViewManager).mQwertyMatrixMode != qwertymatrixmode) {
					isrestart = true;
				}
			}
			/* check fullscreen mode */
			{
				final boolean fullscreenmode = pref.getBoolean("fullscreen", false);
				if (mInputViewFullScreenInLandscape != fullscreenmode) {
					isrestart = true;
				}
			}
			/* check tsu mode */
			{
				final boolean tsumode = pref.getBoolean("tsu_du_ltu", false);
				if (((DefaultSoftKeyboard)mInputViewManager).mTsuMode != tsumode) {
					isrestart = true;
				}
			}
			/* check space_below_keyboard mode */
			{
				final boolean mode = pref.getBoolean("space_below_keyboard", false);
				if (((DefaultSoftKeyboard)mInputViewManager).mSpaceBelowKeyboard != mode) {
					isrestart = true;
				}
			}
			/* check nicoflick_mode mode */
			{
				final int mode = Integer.valueOf(getOrientPrefString(pref, "nicoflick_mode", "0"));
				if (((DefaultSoftKeyboard)mInputViewManager).mFlickNicoInput != mode) {
					isrestart = true;
				}
			}
			/* check flick_guide mode */
			{
				final boolean mode = getOrientPrefBoolean(pref, "flick_guide", true);
				if (((DefaultSoftKeyboard)mInputViewManager).mFlickGuide != mode) {
					isrestart = true;
				}
			}

			/* check change_alphanum_12key mode */
			{
				final boolean mode = pref.getBoolean("change_alphanum_12key", false);
				if (((DefaultSoftKeyboard)mInputViewManager).mAnum12Key != mode) {
					isrestart = true;
				}
			}
			/* check change_kana_12key mode */
			{
				final boolean mode = pref.getBoolean("change_kana_12key", false);
				if (((DefaultSoftKeyboard)mInputViewManager).mKana12Key != mode) {
					isrestart = true;
				}
			}
			/* check change_kana_12key mode */
			{
				final boolean mode = getOrientPrefBoolean(pref, "candidate_leftrightkey", true);
				if (mUseLeftRightKeyCandidateSelection != mode) {
					isrestart = true;
				}
			}
		}
		/**
		 * check restart
		 */
		if (true == isrestart) {
			startSoftKeyboard();
			setInputView(super.onCreateInputView());
		}

		final EngineState state = new EngineState();
		state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_NONE;
		updateEngineState(state);

		mPrevCommitCount = 0;
		clearCommitInfo();

		/* load preferences */
		/*
		if (true == pref.getBoolean("reset_symbol", false)) {
			if (mConverterSymbolEngineBack != null) {
				mConverterSymbolEngineBack.close();
				mConverterSymbolEngineBack = null;
			}
			pref.edit().putBoolean("reset_symbol", false);
			pref.edit().commit();
		}
		 */
		// if (mConverterSymbolEngineBack == null) {
			mConverterSymbolEngineBack = new SymbolList(this, SymbolList.LANG_JA);
		// }

		/* start inputview */
		((DefaultSoftKeyboard) mInputViewManager).resetCurrentKeyboard();

		super.onStartInputView(attribute, restarting);

		/* initialize views */
		mCandidatesViewManager.clearCandidates();
		mDocomoEmojiCount = 0;
		resetPrediction();

		/* initialize status */
		mStatus = STATUS_INIT;
		mExactMatchMode = false;

		/* hardware keyboard support */
		mHardShift = 0;
		mHardAlt   = 0;
		updateMetaKeyStateDisplay();

		/* initialize the engine's state */
		fitInputType(pref, attribute);

		((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(true);

		if (isEnableL2Converter()) {
			breakSequence();
		}
	}

	/** @see net.gorry.android.input.nicownng.NicoWnnG#hideWindow */
	@Override public void hideWindow() {
		mCandidatesViewManager.hideCandidateTask();	// close AsyncTask

		mComposingText.clear();
		mInputViewManager.onUpdateState(this);
		clearCommitInfo();
		mHandler.removeMessages(MSG_START_TUTORIAL);
		mInputViewManager.closing();
		if (mTutorial != null) {
			mTutorial.close();
			mTutorial = null;
		}
		super.hideWindow();
	}
	/*
	 *
	 */
	public void resetSymbol() {
		if (mConverterSymbolEngineBack != null) {
			mConverterSymbolEngineBack.close();
			mConverterSymbolEngineBack = null;
		}
		if (mConverterSymbolEngineBack == null) {
			mConverterSymbolEngineBack = new SymbolList(this, SymbolList.LANG_JA);
		}
	}
	/*
	 *
	 */
	private void resetPrediction() {
		mPredictionCount = 0;
		if (kPREDICTION_ON == mPredictionMode) {
			mActionPrediction = true;
			mPredictionDelayMS1st = PREDICTION_DELAY_MS_1ST_00;
			mPredictionDelayMSShowingCandidate = PREDICTION_DELAY_MS_SHOWING_CANDIDATE_00;
		}
		else{
			mActionPrediction = false;
			mPredictionDelayMS1st = PREDICTION_DELAY_MS_1ST_01;
			mPredictionDelayMSShowingCandidate = PREDICTION_DELAY_MS_SHOWING_CANDIDATE_01;
		}
	}
	/*
	 *
	 */
	public void startSoftKeyboard() {
		if (null != mInputViewManager) {
			mInputViewManager.closing();
		}
		if (mInputViewMode.equals(INPUTMODE_NICO)) {
			final SetupKeyboardNico keyboardnico = new SetupKeyboardNico();
			mInputViewManager  = new DefaultSoftKeyboardNico(keyboardnico);
		}
		else if (mInputViewMode.equals(INPUTMODE_BELL)) {
			final SetupKeyboardBell keyboardbell = new SetupKeyboardBell();
			mInputViewManager  = new DefaultSoftKeyboardNico(keyboardbell);
		}
		else if (mInputViewMode.equals(INPUTMODE_NORMAL)) {
			mInputViewManager  = new DefaultSoftKeyboardJAJP();
		}
		else if (mInputViewMode.equals(INPUTMODE_NICO2)) {
			final SetupKeyboardNico2 keyboardnico2 = new SetupKeyboardNico2();
			mInputViewManager  = new DefaultSoftKeyboardNico2(keyboardnico2);
		}
		else if (mInputViewMode.equals(INPUTMODE_2TOUCH)) {
			final SetupKeyboard2Touch keyboard2touch = new SetupKeyboard2Touch();
			mInputViewManager  = new DefaultSoftKeyboardNico(keyboard2touch);
		}
		/*
			else if (mInputViewMode.equals(INPUTMODE_TEST)) {
				final SetupKeyboardNico22 keyboardnico22 = new SetupKeyboardNico22();
				mInputViewManager  = new DefaultSoftKeyboardTest(keyboardnico22);
			}
		 */
		else{
			mInputViewManager  = new DefaultSoftKeyboardJAJP();
		}
	}
	/** @see net.gorry.android.input.nicownng.NicoWnnG#onUpdateSelection */
	@Override public void onUpdateSelection(final int oldSelStart, final int oldSelEnd, final int newSelStart, final int newSelEnd, final int candidatesStart, final int candidatesEnd) {

		mComposingStartCursor = (candidatesStart < 0) ? newSelEnd : candidatesStart;

		if (newSelStart != newSelEnd) {
			clearCommitInfo();
		}

		if (mHasContinuedPrediction) {
			mHasContinuedPrediction = false;
			if (0 < mPrevCommitCount) {
				mPrevCommitCount--;
			}
			return;
		}

		final boolean isNotComposing = ((candidatesStart < 0) && (candidatesEnd < 0));
		if ((mComposingText.size(ComposingText.LAYER1) != 0)
				&& !isNotComposing) {
			updateViewStatus(mTargetLayer, false, true, false);
		} else {
			if (0 < mPrevCommitCount) {
				mPrevCommitCount--;
			} else {
				final int commitEnd = mCommitStartCursor + mPrevCommitText.length();
				if ((((newSelEnd < oldSelEnd) || (commitEnd < newSelEnd)) && clearCommitInfo())
						|| isNotComposing) {
					if (isEnableL2Converter()) {
						breakSequence();
					}

					if (mInputConnection != null) {
						if (isNotComposing && (mComposingText.size(ComposingText.LAYER1) != 0)) {
							mInputConnection.finishComposingText();
						}
					}
					initializeScreen();
				}
			}
		}
	}

	/** @see net.gorry.android.input.nicownng.NicoWnnG#onConfigurationChanged */
	@Override public void onConfigurationChanged(final Configuration newConfig) {
		try {
			super.onConfigurationChanged(newConfig);

			if (mInputConnection != null) {
				if (super.isInputViewShown()) {
					updateViewStatus(mTargetLayer, true, true, false);
				}

				/* display orientation */
				if (mOrientation != newConfig.orientation) {
					mOrientation = newConfig.orientation;
					commitConvertingText();
					initializeScreen();
				}
				/* Hardware keyboard */
				final int hardkeyState = newConfig.keyboard;
				final boolean hardkey  = (hardkeyState >= Configuration.KEYBOARD_QWERTY);
				final int hiddenState = newConfig.hardKeyboardHidden;
				final boolean hidden  = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
				//((DefaultSoftKeyboard) mInputViewManager).setHardKeyboardHidden(hidden, hardkey);
				mEnableTutorial = hidden;
			}
			hideWindow();
		} catch (final Exception ex) {
			/* do nothing if an error occurs. */
		}
	}

	/** @see net.gorry.android.input.nicownng.NicoWnnG#onEvent */
	@Override synchronized public boolean onEvent(final NicoWnnGEvent ev) {

		EngineState state;

		/* handling events which are valid when InputConnection is not active. */
		switch (ev.code) {

			case NicoWnnGEvent.KEYUP:
				onKeyUpEvent(ev.keyEvent);
				return true;

			case NicoWnnGEvent.INITIALIZE_LEARNING_DICTIONARY:
				mConverterEN.initializeDictionary(WnnEngine.DICTIONARY_TYPE_LEARN);
				mConverterJAJP.initializeDictionary(WnnEngine.DICTIONARY_TYPE_LEARN);
				return true;

			case NicoWnnGEvent.INITIALIZE_USER_DICTIONARY:
				return mConverterJAJP.initializeDictionary( WnnEngine.DICTIONARY_TYPE_USER );

			case NicoWnnGEvent.LIST_WORDS_IN_USER_DICTIONARY:
				mUserDictionaryWords = mConverterJAJP.getUserDictionaryWords( );
				return true;

			case NicoWnnGEvent.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 NicoWnnGEvent.ADD_WORD:
				mConverterJAJP.addWord(ev.word);
				return true;

			case NicoWnnGEvent.DELETE_WORD:
				mConverterJAJP.deleteWord(ev.word);
				return true;

			case NicoWnnGEvent.CHANGE_MODE:
				changeEngineMode(ev.mode);
				if (!((ev.mode == ENGINE_MODE_SYMBOL) || (ev.mode == ENGINE_MODE_DOCOMOSYMBOL) || (ev.mode == ENGINE_MODE_EISU_KANA))) {
					initializeScreen();
				}
				return true;

			case NicoWnnGEvent.UPDATE_CANDIDATE:
				if (mEngineState.isRenbun()||mEngineState.isPredict()) {
					mComposingText.setCursor(ComposingText.LAYER1,
							mComposingText.toString(ComposingText.LAYER1).length());
					mExactMatchMode = false;
					updateViewStatusForPrediction(true, true, true);
				} else {
					updateViewStatus(mTargetLayer, true, true, true);
				}
				return true;

			case NicoWnnGEvent.CHANGE_INPUT_VIEW:
				setInputView(onCreateInputView());
				return true;

			case NicoWnnGEvent.CANDIDATE_VIEW_TOUCH:
				boolean ret;
				ret = ((TextCandidatesViewManager)mCandidatesViewManager).onTouchSync();
				return ret;

			case NicoWnnGEvent.TOUCH_OTHER_KEY:
				mStatus |= STATUS_INPUT_EDIT;
				return true;

			case NicoWnnGEvent.RESET_STATUS:
				mStatus = STATUS_INIT;
				return true;

			default:
				break;
		}

		final KeyEvent keyEvent = ev.keyEvent;
		int keyCode = 0;
		if (keyEvent != null) {
			keyCode = keyEvent.getKeyCode();
			// Log.d("NicoWnnG", "keyCode="+keyCode);
		}

		if (mDirectInputMode) {
			if ((ev.code == NicoWnnGEvent.INPUT_SOFT_KEY) && (mInputConnection != null)) {
				mInputConnection.sendKeyEvent(keyEvent);
				mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
						keyEvent.getKeyCode()));
			}

			/* return if InputConnection is not active */
			return false;
		}

		{
			boolean cci = true;
			if (ev.code == NicoWnnGEvent.COMMIT_COMPOSING_TEXT) {
				cci = false;
			}
			switch (keyCode) {
			case KeyEvent.KEYCODE_SHIFT_LEFT:
			case KeyEvent.KEYCODE_SHIFT_RIGHT:
			case KeyEvent.KEYCODE_ALT_LEFT:
			case KeyEvent.KEYCODE_ALT_RIGHT:
				cci = false;
				break;
			case KeyEvent.KEYCODE_DPAD_UP:
			case KeyEvent.KEYCODE_DPAD_DOWN:
				if (isCandidatesViewShown()) {
					cci = false;
				}
				break;
			case KeyEvent.KEYCODE_DPAD_LEFT:
			case KeyEvent.KEYCODE_DPAD_RIGHT:
				if (isCandidatesViewShown()) {
					if (mCandidatesViewManager.getIndicateCandidateView() > 0) {
						if (mUseLeftRightKeyCandidateSelection) {
							cci = false;
						}
					}
				}
				break;
			case KeyEvent.KEYCODE_DPAD_CENTER:
			case KeyEvent.KEYCODE_ENTER:
				if (isCandidatesViewShown()) {
					if (mEngineState.isSymbolList() || mEngineState.isDocomoSymbolList()) {
						cci = false;
					}
				}
				break;
			case KeyEvent.KEYCODE_SPACE:
				if (mCandidatesViewManager.getIndicateCandidateView() > 0) {
					cci = false;
				}
				if ((keyEvent != null) && (keyEvent.isAltPressed())) {
					cci = false;
				}
				break;
			}
			if (cci) {
				clearCommitInfo();
			}
		}

		/* change back the dictionary if necessary */
		{
			boolean ues = true;
			switch (ev.code) {
			case NicoWnnGEvent.SELECT_CANDIDATE:
			case NicoWnnGEvent.LIST_CANDIDATES_NORMAL:
			case NicoWnnGEvent.LIST_CANDIDATES_FULL:
				ues = false;
			}
			switch (keyCode) {
			case KeyEvent.KEYCODE_SHIFT_LEFT:
			case KeyEvent.KEYCODE_SHIFT_RIGHT:
			case KeyEvent.KEYCODE_ALT_LEFT:
			case KeyEvent.KEYCODE_ALT_RIGHT:
				ues = false;
				break;
			case KeyEvent.KEYCODE_DPAD_UP:
			case KeyEvent.KEYCODE_DPAD_DOWN:
				if (isCandidatesViewShown()) {
					ues = false;
				}
				break;
			case KeyEvent.KEYCODE_DPAD_LEFT:
			case KeyEvent.KEYCODE_DPAD_RIGHT:
				if (isCandidatesViewShown()) {
					if (mCandidatesViewManager.getIndicateCandidateView() > 0) {
						if (mUseLeftRightKeyCandidateSelection) {
							ues = false;
						}
					}
				}
				break;
			case KeyEvent.KEYCODE_DPAD_CENTER:
			case KeyEvent.KEYCODE_ENTER:
				if (isCandidatesViewShown()) {
					if (mEngineState.isSymbolList() || mEngineState.isDocomoSymbolList()) {
						ues = false;
					}
				}
				break;
			case KeyEvent.KEYCODE_BACK:
				if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
					ues = false;
				}
				break;
			case KeyEvent.KEYCODE_SPACE:
				if (mCandidatesViewManager.getIndicateCandidateView() > 0) {
					ues = false;
				}
				if ((keyEvent != null) && (keyEvent.isAltPressed())) {
					ues = false;
				}
				break;
			case DefaultSoftKeyboard.KEYCODE_IS01_MOJI:
			case DefaultSoftKeyboard.KEYCODE_IS01_E_KAO_KI:
			case DefaultSoftKeyboard.KEYCODE_IS11SH_MOJI:
			case DefaultSoftKeyboard.KEYCODE_IS11SH_E_KAO_KI:
			case DefaultSoftKeyboard.KEYCODE_007SH_MOJI:
			case DefaultSoftKeyboard.KEYCODE_007SH_E_KAO_KI:
				if (mIs01Enable) {
					ues = false;
				}
				break;
			}
			if (ues) {
				state = new EngineState();
				state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_NONE;
				updateEngineState(state);
			}
		}

		if (ev.code == NicoWnnGEvent.LIST_CANDIDATES_FULL) {
			mStatus |= STATUS_CANDIDATE_FULL;
			mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_FULL);
			return true;
		}
		if (ev.code == NicoWnnGEvent.LIST_CANDIDATES_NORMAL) {
			mStatus &= ~STATUS_CANDIDATE_FULL;
			mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
			return true;
		}

		boolean ret = false;
		switch (ev.code) {
			case NicoWnnGEvent.INPUT_CHAR:
				if ((mPreConverter == null) && !isEnableL2Converter()) {
					/* direct input (= full-width alphabet/number input) */
					commitText(false);
					commitText(new String(ev.chars));
					mCandidatesViewManager.clearCandidates();
					mDocomoEmojiCount = 0;
					resetPrediction();
				} else if (!isEnableL2Converter()) {
					processSoftKeyboardCodeWithoutConversion(ev.chars);
				} else {
					processSoftKeyboardCode(ev.chars);
				}
				ret = true;
				break;

			case NicoWnnGEvent.TOGGLE_CHAR:
				processSoftKeyboardToggleChar(ev.toggleTable);
				ret = true;
				break;

			case NicoWnnGEvent.TOGGLE_REVERSE_CHAR:
				if (((mStatus & ~STATUS_CANDIDATE_FULL) == STATUS_INPUT)
						&& !(mEngineState.isConvertState())) {

					final int cursor = mComposingText.getCursor(ComposingText.LAYER1);
					if (cursor > 0) {
						final String prevChar = mComposingText.getStrSegment(ComposingText.LAYER1, cursor - 1).string;
						final String c = searchToggleCharacter(prevChar, ev.toggleTable, true);
						if (c != null) {
							mComposingText.delete(ComposingText.LAYER1, false);
							appendStrSegment(new StrSegment(c));
							updateViewStatusForPrediction(true, true, true);
							ret = true;
							break;
						}
					}
				}
				break;

			case NicoWnnGEvent.REPLACE_CHAR:
				final int cursor = mComposingText.getCursor(ComposingText.LAYER1);
				if ((cursor > 0)
						&& !(mEngineState.isConvertState())) {

					final String search = mComposingText.getStrSegment(ComposingText.LAYER1, cursor - 1).string;
					final String c = (String)ev.replaceTable.get(search);
					if (c != null) {
						mComposingText.delete(1, false);
						appendStrSegment(new StrSegment(c));
						updateViewStatusForPrediction(true, true, true);
						ret = true;
						mStatus = STATUS_INPUT_EDIT;
						break;
					}
				}
				break;

			case NicoWnnGEvent.INPUT_KEY:
//				final DefaultSoftKeyboardJAJP inputManager = ((DefaultSoftKeyboardJAJP) mInputViewManager);
				final DefaultSoftKeyboard inputManager = ((DefaultSoftKeyboard) mInputViewManager);
				/* update shift/alt state */
				switch (keyCode) {
					case KeyEvent.KEYCODE_0:
						if (mIs12keyEnable) {
							if (inputManager != null) {
								if (inputManager.getKeyboardType() == DefaultSoftKeyboard.KEYBOARD_12KEY) {
									inputManager.onKey(
											DefaultSoftKeyboard.KEYCODE_JP12_0,
											null
									);
									return true;
								}
							}
						}
						break;

					case KeyEvent.KEYCODE_1:
					case KeyEvent.KEYCODE_2:
					case KeyEvent.KEYCODE_3:
					case KeyEvent.KEYCODE_4:
					case KeyEvent.KEYCODE_5:
					case KeyEvent.KEYCODE_6:
					case KeyEvent.KEYCODE_7:
					case KeyEvent.KEYCODE_8:
					case KeyEvent.KEYCODE_9:
						if (mIs12keyEnable) {
							if (inputManager != null) {
								if (inputManager.getKeyboardType() == DefaultSoftKeyboard.KEYBOARD_12KEY) {
									inputManager.onKey(
											DefaultSoftKeyboard.KEYCODE_JP12_1 - (keyCode-KeyEvent.KEYCODE_1),
											null
									);
									return true;
								}
							}
						}
						break;

					case KeyEvent.KEYCODE_STAR:
						if (mIs12keyEnable) {
							if (inputManager != null) {
								if (inputManager.getKeyboardType() == DefaultSoftKeyboard.KEYBOARD_12KEY) {
									inputManager.onKey(
											DefaultSoftKeyboard.KEYCODE_JP12_ASTER,
											null
									);
									return true;
								}
							}
						}
						break;

					case KeyEvent.KEYCODE_POUND:
						if (mIs12keyEnable) {
							if (inputManager != null) {
								if (inputManager.getKeyboardType() == DefaultSoftKeyboard.KEYBOARD_12KEY) {
									inputManager.onKey(
											DefaultSoftKeyboard.KEYCODE_JP12_SHARP,
											null
									);
									return true;
								}
							}
						}
						break;
				
					case KeyEvent.KEYCODE_DEL:
						if (mIs12keyEnable) {
							if (inputManager != null) {
								if (inputManager.getKeyboardType() == DefaultSoftKeyboard.KEYBOARD_12KEY) {
									inputManager.onKey(
											DefaultSoftKeyboard.KEYCODE_JP12_BACKSPACE,
											null
									);
									return true;
								}
							}
						}
						break;
				
					case KeyEvent.KEYCODE_ALT_LEFT:
					case KeyEvent.KEYCODE_ALT_RIGHT:
						if (keyEvent.getRepeatCount() == 0) {
							if (++mHardAlt > 2) { mHardAlt = 0; }
							mAltPressing   = true;
							updateMetaKeyStateDisplay();
						}
						return true;

					case KeyEvent.KEYCODE_SHIFT_LEFT:
					case KeyEvent.KEYCODE_SHIFT_RIGHT:
						if (keyEvent.getRepeatCount() == 0) {
							if (++mHardShift > 2) { mHardShift = 0; }
							mShiftPressing = true;
							updateMetaKeyStateDisplay();
						}
						return true;

					case DefaultSoftKeyboard.KEYCODE_IS01_MOJI:
					case DefaultSoftKeyboard.KEYCODE_007SH_MOJI:
					case DefaultSoftKeyboard.KEYCODE_IS11SH_MOJI:
						if (keyEvent.getRepeatCount() == 0) {
							evHardwareKeyboardIS01MojiKey = ev;
							synchronized (this) {
								if (!mRegisterLongPressHardwareKeyboardIS01MojiKey) {
									mRegisterLongPressHardwareKeyboardIS01MojiKey = true;
									mHandlerLongPressHardwareKeyboardIS01MojiKey.postDelayed(mActionLongPressHardwareKeyboardIS01MojiKey, mTimeOutLongPressHardwareKeyboardIS01MojiKey);
								}
							}
						}
						return true;

					case DefaultSoftKeyboard.KEYCODE_IS01_E_KAO_KI:
					case DefaultSoftKeyboard.KEYCODE_007SH_E_KAO_KI:
					case DefaultSoftKeyboard.KEYCODE_IS11SH_E_KAO_KI:
						if (keyEvent.getRepeatCount() == 0) {
							evHardwareKeyboardIS01EKaoKiKey = ev;
							synchronized (this) {
								if (!mRegisterLongPressHardwareKeyboardIS01EKaoKiKey) {
									mRegisterLongPressHardwareKeyboardIS01EKaoKiKey = true;
									mHandlerLongPressHardwareKeyboardIS01EKaoKiKey.postDelayed(mActionLongPressHardwareKeyboardIS01EKaoKiKey, mTimeOutLongPressHardwareKeyboardIS01EKaoKiKey);
								}
							}
						}
						return true;
				}

				/* handle other key event */
				ret = processKeyEvent(keyEvent);
				break;

			case NicoWnnGEvent.INPUT_SOFT_KEY:
				ret = processKeyEvent(keyEvent);
				if (!ret) {
					mInputConnection.sendKeyEvent(keyEvent);
					mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyEvent.getKeyCode()));
					ret = true;
				}
				break;

			case NicoWnnGEvent.SELECT_CANDIDATE:
				initCommitInfoForWatchCursor();
				if (isEnglishPrediction()) {
					mComposingText.clear();
				}
				mStatus = commitText(ev.word);
				if (isEnglishPrediction() && !mEngineState.isSymbolList() && !mEngineState.isDocomoSymbolList() && mEnableAutoInsertSpace) {
					commitSpaceJustOne();
				}
				checkCommitInfo();

				if (mEngineState.isSymbolList() || mEngineState.isDocomoSymbolList()) {
					mEnableAutoDeleteSpace = false;
					mCandidatesViewManager.setIndicateCandidateView();
				} else {
					mCandidatesViewManager.clearIndicateCandidateView();
					if (!mEnablePredictionAfterEnter) {
						initializeScreen();
					}
				}
				if (!mEngineState.isDocomoSymbolList()) {
					mDocomoEmojiCount = 0;
				}
				break;

			case NicoWnnGEvent.CONVERT:
				if ((kPREDICTION_ON == mPredictionMode) || (kPREDICTION_OFF == mPredictionMode)) {
					startConvert(EngineState.CONVERT_TYPE_RENBUN);
				}
				else{
					//Log.d("NicoWnnG", "convert!!! [" + String.valueOf(mPredictionCount) + "]\n");
					if (0 == mPredictionCount) {
						mActionPrediction = true;
						updateViewStatusForPrediction(true, true, true);
					}
					else{
						mActionPrediction = false;
						startConvert(EngineState.CONVERT_TYPE_RENBUN);
						mActionPrediction = true; // enable prediction
					}
					mPredictionCount = (mPredictionCount+1)&1;
				}
				break;

			case NicoWnnGEvent.CONVERT_PREDICT:
				if ((kPREDICTION_ON == mPredictionMode) || (kPREDICTION_OFF == mPredictionMode)) {
					startConvert(EngineState.CONVERT_TYPE_PREDICT);
				}
				else{
					//Log.d("NicoWnnG", "convert!!! [" + String.valueOf(mPredictionCount) + "]\n");
					if (0 == mPredictionCount) {
						mActionPrediction = true;
						updateViewStatusForPrediction(true, true, true);
					}
					else{
						mActionPrediction = false;
						startConvert(EngineState.CONVERT_TYPE_RENBUN);
						mActionPrediction = true; // enable prediction
					}
					mPredictionCount = (mPredictionCount+1)&1;
				}
				break;

			case NicoWnnGEvent.CONVERT_PREDICT_BACKWARD:
				if ((kPREDICTION_ON == mPredictionMode) || (kPREDICTION_OFF == mPredictionMode)) {
					startConvert(EngineState.CONVERT_TYPE_PREDICT, true);
				}
				else{
					//Log.d("NicoWnnG", "convert!!! [" + String.valueOf(mPredictionCount) + "]\n");
					if (0 == mPredictionCount) {
						mActionPrediction = true;
						updateViewStatusForPrediction(true, true, true);
					}
					else{
						mActionPrediction = false;
						startConvert(EngineState.CONVERT_TYPE_RENBUN, true);
						mActionPrediction = true; // enable prediction
					}
					mPredictionCount = (mPredictionCount+1)&1;
				}
				break;

			case NicoWnnGEvent.COMMIT_COMPOSING_TEXT:
				commitAllText();
				break;
		}

		return ret;
	}

	/** @see net.gorry.android.input.nicownng.NicoWnnG#onEvaluateFullscreenMode */
	@Override public boolean onEvaluateFullscreenMode() {
		final int hiddenState = getResources().getConfiguration().hardKeyboardHidden;
		final boolean hidden  = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
		if (!hidden) {
			return false;
		}
		if (mInputViewFullScreenInLandscape) {
			final Configuration config = getResources().getConfiguration();
			return (config.orientation == Configuration.ORIENTATION_LANDSCAPE);
		}
		/* never use full-screen mode */
		return false;
	}

	/** @see net.gorry.android.input.nicownng.NicoWnnG#onEvaluateInputViewShown */
	@Override public boolean onEvaluateInputViewShown() {
		return true;
	}

	/**
	 * Get the instance of this service.
	 * <br>
	 * Before using this method, the constructor of this service must be invoked.
	 *
	 * @return      The instance of this service
	 */
	public static NicoWnnGJAJP getInstance() {
		return mSelf;
	}

	/**
	 * Create a {@link StrSegment} from a character code.
	 * <br>
	 * @param charCode           A character code
	 * @return                  {@link StrSegment} created; {@code null} if an error occurs.
	 */
	private StrSegment createStrSegment(final int charCode) {
		if (charCode == 0) {
			return null;
		}
		return new StrSegment(Character.toChars(charCode));
	}

	/**
	 * Key event handler.
	 *
	 * @param ev        A key event
	 * @return  {@code true} if the event is handled in this method.
	 */
	private boolean processKeyEvent(final KeyEvent ev) {
		final int key = ev.getKeyCode();

		/* 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() == true) && (ev.isShiftPressed() == true))) {
				final 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;
				}
			}

			commitConvertingText();

			final EditorInfo edit = getCurrentInputEditorInfo();
			StrSegment str;

			/* get the key character */
			if ((mHardShift== 0) && (mHardAlt == 0)) {
				/* no meta key is locked */
				final int shift = (mAutoCaps)? getShiftKeyState(edit) : 0;
				if ((shift != mHardShift) && ((key >= KeyEvent.KEYCODE_A) && (key <= KeyEvent.KEYCODE_Z))) {
					/* handling auto caps for a alphabet character */
					str = createStrSegment(ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON));
				} else {
					str = createStrSegment(ev.getUnicodeChar());
				}
			} else {
				str = createStrSegment(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.isShiftPressed()) {
					updateMetaKeyStateDisplay();
				}
			}

			if (str == null) {
				return true;
			}

			/* append the character to the composing text if the character is not TAB */
			if (str.string.charAt(0) != '\u0009') {
				processHardwareKeyboardInputChar(str);
				return true;
			} else {
				commitText(true);
				commitText(str.string);
				initializeScreen();
				return true;
			}

		} else if (key == KeyEvent.KEYCODE_SPACE) {
			/* H/W space key */
			//if (mCandidatesViewManager.getIndicateCandidateView() == 0) {
			if (!isCandidatesViewShown()) {
				processHardwareKeyboardSpaceKey(ev);
				return true;
			}

		} else if (key == KeyEvent.KEYCODE_SYM) {
			/* display the symbol list */
			if (mPreConverter != null) {
				mPreConverter.convert2(mComposingText);
			}
			initCommitInfoForWatchCursor();
			mStatus = commitText(true);
			checkCommitInfo();
			changeEngineMode(ENGINE_MODE_SYMBOL);
			mHardAlt = 0;
			updateMetaKeyStateDisplay();
			return true;
		} else if ((mIs01Enable) && (key == DefaultSoftKeyboard.KEYCODE_IS01_MOJI)) {  // KeyEvent.KEYCODE_IS01_MOJI
			/* IS01 [moji] key */
			processHardwareKeyboardIS01MojiKey(ev);
			return true;
		} else if ((mIs01Enable) && (key == DefaultSoftKeyboard.KEYCODE_IS01_E_KAO_KI)) {  // KeyEvent.KEYCODE_IS01_E_KAO_KI
			/* IS01 [e/kao/ki] key */
			processHardwareKeyboardIS01EKaoKiKey(ev);
			return true;
		} else if ((mIs01Enable) && (key == DefaultSoftKeyboard.KEYCODE_IS11SH_MOJI)) {  // KeyEvent.KEYCODE_IS11SH_MOJI
			/* IS11SH [moji] key */
			processHardwareKeyboardIS01MojiKey(ev);
			return true;
		} else if ((mIs01Enable) && (key == DefaultSoftKeyboard.KEYCODE_IS11SH_E_KAO_KI)) {  // KeyEvent.KEYCODE_IS11SH_E_KAO_KI
			/* IS11SH [e/kao/ki] key */
			processHardwareKeyboardIS01EKaoKiKey(ev);
			return true;
		} else if ((mIs01Enable) && (key == DefaultSoftKeyboard.KEYCODE_007SH_MOJI)) {  // KeyEvent.KEYCODE_007SH_MOJI
			/* 007SH [moji] key */
			processHardwareKeyboardIS01MojiKey(ev);
			return true;
		} else if ((mIs01Enable) && (key == DefaultSoftKeyboard.KEYCODE_007SH_E_KAO_KI)) {  // KeyEvent.KEYCODE_007SH_E_KAO_KI
			/* 007SH [e/kao/ki] key */
			processHardwareKeyboardIS01EKaoKiKey(ev);
			return true;
		}

		/* Functional key */
		if (mComposingText.size(ComposingText.LAYER1) > 0) {
			switch (key) {
				case KeyEvent.KEYCODE_DEL:
					mStatus = STATUS_INPUT_EDIT;
					if (mEngineState.isConvertState()) {
						mComposingText.setCursor(ComposingText.LAYER1,
								mComposingText.toString(ComposingText.LAYER1).length());
						mExactMatchMode = false;
					} else {
						if (mComposingText.size(ComposingText.LAYER1) == 1) {
							initializeScreen();
							return true;
						} else {
							mComposingText.delete(ComposingText.LAYER1, false);
						}
					}
					// updateViewStatusForPrediction(true, true, true);
					updateViewStatusForPrediction(true, true, false);
					return true;

				case KeyEvent.KEYCODE_BACK:
					if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
						mStatus &= ~STATUS_CANDIDATE_FULL;
						mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
					} else {
						if (!mEngineState.isConvertState()) {
							initializeScreen();
							if (mConverter != null) {
								mConverter.init();
							}
						} else {
							mCandidatesViewManager.clearCandidates();
							mDocomoEmojiCount = 0;
							resetPrediction();
							mStatus = STATUS_INPUT_EDIT;
							mExactMatchMode = false;
							mComposingText.setCursor(ComposingText.LAYER1,
									mComposingText.toString(ComposingText.LAYER1).length());
							updateViewStatusForPrediction(true, true, true);
						}
					}
					return true;

				case KeyEvent.KEYCODE_DPAD_UP:
					if (!isEnableL2Converter()) {
						commitText(false);
						return false;
					}
					if ((mPreConverter != null) && !(mEngineState.isRenbun()||mEngineState.isPredict())) {
						if (mComposingText.size(0) != 0) {
							final DefaultSoftKeyboard inputManager = ((DefaultSoftKeyboard) mInputViewManager);
							if (inputManager != null) {
								inputManager.onKey(
										DefaultSoftKeyboard.KEYCODE_JP12_CONVPREDICT_BACKWARD,
										null
								);
								return true;
							}
							// break;
						}
						return true;
					}
					{
						WnnWord s;
						s = mCandidatesViewManager.getUpWnnWordCandidate();
						processChangeWnnWordCandidate(s);
					}
					return true;

				case KeyEvent.KEYCODE_DPAD_DOWN:
					if (!isEnableL2Converter()) {
						commitText(false);
						return false;
					}
					if ((mPreConverter != null) && !(mEngineState.isRenbun()||mEngineState.isPredict())) {
						if (mComposingText.size(0) != 0) {
							final DefaultSoftKeyboard inputManager = ((DefaultSoftKeyboard) mInputViewManager);
							if (inputManager != null) {
								inputManager.onKey(
										DefaultSoftKeyboard.KEYCODE_JP12_CONVPREDICT,
										null
								);
								return true;
							}
							// break;
						}
						return true;
					}
					{
						WnnWord s;
						s = mCandidatesViewManager.getDownWnnWordCandidate();
						processChangeWnnWordCandidate(s);
					}
					return true;

				case KeyEvent.KEYCODE_SPACE:
					if (!isEnableL2Converter()) {
						commitText(false);
						return false;
					}
					{
						WnnWord s;
						if (!inputWithShift(ev)) {
							s = mCandidatesViewManager.getNextWnnWordCandidate();
						} else {
							s = mCandidatesViewManager.getPrevWnnWordCandidate();
						}
						processChangeWnnWordCandidate(s);
					}
					return true;

/*
				case KeyEvent.KEYCODE_DPAD_LEFT:
					if (!isEnableL2Converter()) {
						if (mEngineState.keyboard == EngineState.KEYBOARD_12KEY) {
							commitText(false);
						}
					} else {
						processLeftKeyEvent();
					}
					return true;
				case KeyEvent.KEYCODE_DPAD_RIGHT:
					if (!isEnableL2Converter()) {
						if (mEngineState.keyboard == EngineState.KEYBOARD_12KEY) {
							commitText(false);
						}
					} else {
						processRightKeyEvent();
					}
					return true;
*/
				case KeyEvent.KEYCODE_DPAD_LEFT:
					if (!isEnableL2Converter()) {
						commitText(false);
						return false;
					}
					if (mUseLeftRightKeyCandidateSelection && isCandidatesViewShown() && (mCandidatesViewManager.getIndicateCandidateView() > 0)) {
						if (!inputWithShift(ev)) {
							WnnWord s;
							s = mCandidatesViewManager.getLeftWnnWordCandidate();
							processChangeWnnWordCandidate(s);
							return true;
						}
					}
					processLeftKeyEvent();
					return true;
				case KeyEvent.KEYCODE_DPAD_RIGHT:
					if (!isEnableL2Converter()) {
						commitText(false);
						return false;
					}
					if (mUseLeftRightKeyCandidateSelection && isCandidatesViewShown() && (mCandidatesViewManager.getIndicateCandidateView() > 0)) {
						if (!inputWithShift(ev)) {
							WnnWord s;
							s = mCandidatesViewManager.getRightWnnWordCandidate();
							processChangeWnnWordCandidate(s);
							return true;
						}
					}
					processRightKeyEvent();
					return true;

				case KeyEvent.KEYCODE_DPAD_CENTER:
				case KeyEvent.KEYCODE_ENTER:
					if (!isEnglishPrediction()) {
						final int cursor = mComposingText.getCursor(ComposingText.LAYER1);
						if (cursor < 1) {
							return true;
						}
					}
					initCommitInfoForWatchCursor();
//					mStatus = commitText(mEngineState.convertType != EngineState.CONVERT_TYPE_PREDICT);
					mStatus = commitText(true);
					checkCommitInfo();

					if (isEnglishPrediction() || !mEnablePredictionAfterEnter) {
						if (mComposingText.size(ComposingText.LAYER0) == 0) {
							initializeScreen();
						}
					}

					if (mEnableAutoHideKeyboard) {
						mInputViewManager.closing();
						requestHideSelf(0);
					}
					return true;

				case KeyEvent.KEYCODE_CALL:
					return false;

				default:
					return true;
			}
		} else {
			/* if there is no composing string. */
			if (mCandidatesViewManager.getCurrentView().isShown()) {
				/* displaying relational prediction candidates */
				switch (key) {
					case KeyEvent.KEYCODE_DPAD_LEFT:
						if (mUseLeftRightKeyCandidateSelection && isCandidatesViewShown() && (mCandidatesViewManager.getIndicateCandidateView() > 0)) {
							final WnnWord s = mCandidatesViewManager.getLeftWnnWordCandidate();
							if (s != null) {
								updateViewStatusForPrediction(false, true, true);
								return true;
							}
						}
						if (isEnableL2Converter()) {
							/* initialize the converter */
							mConverter.init();
						}
						mStatus = STATUS_INPUT_EDIT;
						updateViewStatusForPrediction(true, true, true);
						return false;

					case KeyEvent.KEYCODE_DPAD_RIGHT:
						if (mUseLeftRightKeyCandidateSelection && isCandidatesViewShown() && (mCandidatesViewManager.getIndicateCandidateView() > 0)) {
							final WnnWord s = mCandidatesViewManager.getRightWnnWordCandidate();
							if (s != null) {
								updateViewStatusForPrediction(false, true, true);
								return true;
							}
						}
						if (isEnableL2Converter()) {
							/* initialize the converter */
							mConverter.init();
						}
						mStatus = STATUS_INPUT_EDIT;
						updateViewStatusForPrediction(true, true, true);
						return false;

					case KeyEvent.KEYCODE_DPAD_UP:
					{
						final WnnWord s = mCandidatesViewManager.getUpWnnWordCandidate();
						if (s != null) {
							updateViewStatusForPrediction(false, true, true);
							return true;
						}
					}
					break;

					case KeyEvent.KEYCODE_DPAD_DOWN:
					{
						final WnnWord s = mCandidatesViewManager.getDownWnnWordCandidate();
						if (s != null) {
							updateViewStatusForPrediction(false, true, true);
							return true;
						}
					}
					break;

					case KeyEvent.KEYCODE_SPACE:
					{
						final WnnWord s;
						if (!inputWithShift(ev)) {
							s = mCandidatesViewManager.getNextWnnWordCandidate();
						} else {
							s = mCandidatesViewManager.getPrevWnnWordCandidate();
						}
						if (s != null) {
							updateViewStatusForPrediction(false, true, true);
							return true;
						}
					}
					break;

					case KeyEvent.KEYCODE_DPAD_CENTER:
					case KeyEvent.KEYCODE_ENTER:
						final WnnWord s = mCandidatesViewManager.getNowWnnWordCandidate();
						onEvent(new NicoWnnGEvent(NicoWnnGEvent.SELECT_CANDIDATE, s));
						return true;

					default:
						return processKeyEventNoInputCandidateShown(ev);
				}
			} else {
				switch (key) {
					case KeyEvent.KEYCODE_DPAD_CENTER:
					case KeyEvent.KEYCODE_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;
	}

	private boolean inputWithShift(final KeyEvent ev) {
		final DefaultSoftKeyboardJAJP inputManager = ((DefaultSoftKeyboardJAJP) mInputViewManager);
		final KeyboardView v = (KeyboardView)(inputManager.getKeyboardView());
		final boolean softShift = v.isShifted();
		return (ev.isShiftPressed() || softShift);
	}

	private void processChangeWnnWordCandidate(final WnnWord s) {
		if (s != null) {
			final int l0cursor = mComposingText.getCursor(ComposingText.LAYER0);
			final int l1cursor = mComposingText.getCursor(ComposingText.LAYER1);
			mComposingText.setCursor(ComposingText.LAYER2, 0);
			final StrSegment[] ss = new StrSegment[1];
			StrSegment old = mComposingText.getStrSegment(ComposingText.LAYER2, 0);
			ss[0] = new StrSegment(s.candidate, old.from, old.to);
			mComposingText.replaceStrSegment(ComposingText.LAYER2, ss);
			updateViewStatus(ComposingText.LAYER2, false, true, true);
			mComposingText.setCursorDirect(ComposingText.LAYER0, l0cursor);
			mComposingText.setCursorDirect(ComposingText.LAYER1, l1cursor);
			mComposingText.setCursorDirect(ComposingText.LAYER2, 1);
		}
	}

	/**
	 * Handle the space key event from the Hardware keyboard.
	 *
	 * @param ev  The space key event
	 */
	private void processHardwareKeyboardIS01MojiKey(final KeyEvent ev) {
		/* IS01 [moji] key */
		if (mComposingText.size(0) == 0) {
			/* change Japanese <-> English mode */
			mHardAlt = 0;
			mHardShift = 0;
			updateMetaKeyStateDisplay();
			/*
				if (mEngineState.isEnglish()) {
			    	// English mode to Japanese mode
					//((DefaultSoftKeyboard) mInputViewManager).changeKeyMode(DefaultSoftKeyboard.KEYMODE_JA_FULL_HIRAGANA);
					((DefaultSoftKeyboard) mInputViewManager).setDefaultKeyboard();
					mConverter = mConverterJAJP;
				} else {
					// Japanese mode to English mode
					((DefaultSoftKeyboard) mInputViewManager).changeKeyMode(DefaultSoftKeyboard.KEYMODE_JA_HALF_ALPHABET);
					mConverter = mConverterEN;
				}
			 */
			// change next mode
			((DefaultSoftKeyboard) mInputViewManager).nextKeyMode();
			// reset candidate & symbol position
			mCandidatesViewManager.clearCandidates();
			mDocomoEmojiCount = 0;
			resetPrediction();
		} else {
			changeKanaDirectPhase();
		}
	}

	private void processHardwareKeyboardIS01EKaoKiKey(final KeyEvent ev) {
		/* IS01 [E/Kao/Ki] key */
		if (mPreConverter != null) {
			mPreConverter.convert2(mComposingText);
		}
		if (!mEngineState.isSymbolList() && !mEngineState.isDocomoSymbolList()) {
			commitAllText();
		}
		changeEngineMode(ENGINE_MODE_SYMBOL);
	}

	/**
	 * Handle the space key event from the Hardware keyboard.
	 *
	 * @param ev  The space key event
	 */
	private void processHardwareKeyboardSpaceKey(final KeyEvent ev) {
		/* H/W space key */
		if (mPreConverter != null) {
			mPreConverter.convert2(mComposingText);
		}
		if (ev.isShiftPressed()) {
			/* change Japanese <-> English mode */
			mHardAlt = 0;
			mHardShift = 0;
			updateMetaKeyStateDisplay();
			/*
			if (mEngineState.isEnglish()) {
			    // English mode to Japanese mode
				//((DefaultSoftKeyboard) mInputViewManager).changeKeyMode(DefaultSoftKeyboard.KEYMODE_JA_FULL_HIRAGANA);
				((DefaultSoftKeyboard) mInputViewManager).setDefaultKeyboard();
				mConverter = mConverterJAJP;
			} else {
				// Japanese mode to English mode
				((DefaultSoftKeyboard) mInputViewManager).changeKeyMode(DefaultSoftKeyboard.KEYMODE_JA_HALF_ALPHABET);
				mConverter = mConverterEN;
			}
			 */
			// change next mode
			((DefaultSoftKeyboard) mInputViewManager).nextKeyMode();
			// reset candidate & symbol position
			mCandidatesViewManager.clearCandidates();
			mDocomoEmojiCount = 0;
			resetPrediction();
		} else if(ev.isAltPressed()){
			/* display the symbol list (G1 specific. same as KEYCODE_SYM) */
			if (!mEngineState.isSymbolList() && !mEngineState.isDocomoSymbolList()) {
				commitAllText();
			}
			changeEngineMode(ENGINE_MODE_SYMBOL);
			mHardAlt = 0;
			updateMetaKeyStateDisplay();

		} else if (isEnglishPrediction()) {
			/* Auto commit if English mode */
			if (mComposingText.size(0) == 0) {
				commitText(" ");
				mCandidatesViewManager.clearCandidates();
				mDocomoEmojiCount = 0;
				resetPrediction();
				breakSequence();
			} else {
				initCommitInfoForWatchCursor();
				commitText(true);
				commitSpaceJustOne();
				checkCommitInfo();
			}
			mEnableAutoDeleteSpace = false;

		} else {
			/* start consecutive clause conversion if Japanese mode */
			if (mComposingText.size(0) == 0) {
				commitText(" ");
				mCandidatesViewManager.clearCandidates();
				mDocomoEmojiCount = 0;
				resetPrediction();
				breakSequence();
			} else {
				startConvert(EngineState.CONVERT_TYPE_RENBUN);
			}
		}
	}

	/**
	 * Handle the character code from the hardware keyboard except the space key.
	 *
	 * @param str  The input character
	 */
	private void processHardwareKeyboardInputChar(final StrSegment str) {
		if (isEnableL2Converter()) {
			boolean commit = false;
			if (mPreConverter == null) {
				final Matcher m = mEnglishAutoCommitDelimiter.matcher(str.string);
				if (m.matches()) {
					commitText(true);

					commit = true;
				}
				appendStrSegment(str);
			} else {
				appendStrSegment(str);
				mPreConverter.convert(mComposingText);
			}

			if (commit) {
				commitText(true);
			} else {
				mStatus = STATUS_INPUT;
				//updateViewStatusForPrediction(true, true, true);
				updateViewStatusForPrediction(true, true, false);
			}
		} else {
			appendStrSegment(str);
			boolean completed = true;
			if (mPreConverter != null) {
				completed = mPreConverter.convert(mComposingText);
			}

			if (completed) {
				commitText(false);
			} else {
				updateViewStatus(ComposingText.LAYER1, false, true, false);
			}
		}
	}

	/** Thread for updating the candidates view */
	private void updatePrediction() {
		int candidates = 0;
		final int cursor = mComposingText.getCursor(ComposingText.LAYER1);
		if (isEnableL2Converter() || mEngineState.isSymbolList() || mEngineState.isDocomoSymbolList()) {
			if (mExactMatchMode) {
				/* exact matching */
				candidates = mConverter.predict(mComposingText, 0, cursor);
			} else {
				/* normal prediction */
				candidates = mConverter.predict(mComposingText, 0, -1);
			}
		}

		/* update the candidates view */
		if (candidates > 0) {
			mHasContinuedPrediction = ((mComposingText.size(ComposingText.LAYER1) == 0) && !(mEngineState.isSymbolList() || mEngineState.isDocomoSymbolList()));
			mCandidatesViewManager.displayCandidates(mConverter);
		} else {
			mCandidatesViewManager.clearCandidates();
			mDocomoEmojiCount = 0;
			resetPrediction();
		}
	}

	/**
	 * Handle a left key event.
	 */
	private void processLeftKeyEvent() {
		if (mEngineState.isConvertState()) {
			if (mEngineState.isEisuKana()) {
				mExactMatchMode = true;
			}

			if (1 < mComposingText.getCursor(ComposingText.LAYER1)) {
				mComposingText.moveCursor(ComposingText.LAYER1, -1);
			}
		} else if (mExactMatchMode) {
			mComposingText.moveCursor(ComposingText.LAYER1, -1);
		} else {
			if (isEnglishPrediction()) {
				mComposingText.moveCursor(ComposingText.LAYER1, -1);
			} else {
				mExactMatchMode = true;
			}
		}

		mCommitCount = 0; /* retry consecutive clause conversion if necessary. */
		mStatus = STATUS_INPUT_EDIT;

		// updateViewStatus(mTargetLayer, true, true, true);
		updateViewStatus(mTargetLayer, true, true, false);
	}

	/**
	 * Handle a right key event.
	 */
	private void processRightKeyEvent() {
		int layer = mTargetLayer;
		final ComposingText composingText = mComposingText;
		if (mExactMatchMode || (mEngineState.isConvertState())) {
			final int textSize = composingText.size(ComposingText.LAYER1);
			if (composingText.getCursor(ComposingText.LAYER1) == textSize) {
				mExactMatchMode = false;
				layer = ComposingText.LAYER1; /* convert -> prediction */
				final EngineState state = new EngineState();
				state.convertType = EngineState.CONVERT_TYPE_NONE;
				updateEngineState(state);
			} else {
				if (mEngineState.isEisuKana()) {
					mExactMatchMode = true;
				}
				composingText.moveCursor(ComposingText.LAYER1, 1);
			}
		} else {
			if (composingText.getCursor(ComposingText.LAYER1)
					< composingText.size(ComposingText.LAYER1)) {
				composingText.moveCursor(ComposingText.LAYER1, 1);
			}
		}

		mCommitCount = 0; /* retry consecutive clause conversion if necessary. */
		mStatus = STATUS_INPUT_EDIT;

		// updateViewStatus(layer, true, true, true);
		updateViewStatus(layer, true, true, false);
	}

	/**
	 * Handle a key event which is not right or left key when the
	 * composing text is empty and some candidates are shown.
	 *
	 * @param ev        A key event
	 * @return          {@code true} if this consumes the event; {@code false} if not.
	 */
	boolean processKeyEventNoInputCandidateShown(final KeyEvent ev) {
		boolean ret = true;

		switch (ev.getKeyCode()) {
			case KeyEvent.KEYCODE_DEL:
				ret = true;
				break;
			case KeyEvent.KEYCODE_ENTER:
			case KeyEvent.KEYCODE_DPAD_UP:
			case KeyEvent.KEYCODE_DPAD_DOWN:
			case KeyEvent.KEYCODE_MENU:
				ret = false;
				break;

			case KeyEvent.KEYCODE_CALL:
				return false;

			case KeyEvent.KEYCODE_DPAD_CENTER:
				ret = true;
				break;

			case KeyEvent.KEYCODE_BACK:
				if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
					mStatus &= ~STATUS_CANDIDATE_FULL;
					mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
					return true;
				} else {
					ret = true;
				}
				break;

			default:
				return true;
		}

		mCandidatesViewManager.checkCandidateTask();
		if (mConverter != null) {
			/* initialize the converter */
			mConverter.init();
		}
		updateViewStatusForPrediction(true, true, true);
		return ret;
	}

	/**
	 * Update views and the display of the composing text for predict mode.
	 *
	 * @param updateCandidates  {@code true} to update the candidates view
	 * @param updateEmptyText   {@code false} to update the composing text if it is not empty; {@code true} to update always.
	 */
	private void updateViewStatusForPrediction(final boolean updateCandidates, final boolean updateEmptyText, final boolean indicateCandidates) {
		final EngineState state = new EngineState();
		state.convertType = EngineState.CONVERT_TYPE_NONE;
		updateEngineState(state);

		updateViewStatus(ComposingText.LAYER1, updateCandidates, updateEmptyText, indicateCandidates);
	}

	/**
	 * Update views and the display of the composing text.
	 *
	 * @param layer                      Display layer of the composing text
	 * @param updateCandidates  {@code true} to update the candidates view
	 * @param updateEmptyText   {@code false} to update the composing text if it is not empty; {@code true} to update always.
	 */
	private void updateViewStatus(final int layer, final boolean updateCandidates, final boolean updateEmptyText, final boolean indicateCandidates) {
		mTargetLayer = layer;

		if (updateCandidates) {
			updateCandidateView();
		}
		/* notice to the input view */
		mInputViewManager.onUpdateState(this);

		/* set the text for displaying as the composing text */
		mDisplayText.clear();
		mDisplayText.insert(0, mComposingText.toString(layer));

		/* add decoration to the text */
		final int cursor = mComposingText.getCursor(layer);
		if ((mInputConnection != null) && ((mDisplayText.length() != 0) || updateEmptyText)) {
			if (cursor != 0) {
				int highlightEnd = 0;

				if ((mExactMatchMode && (!mEngineState.isEisuKana()))
						|| (FIX_CURSOR_TEXT_END && isEnglishPrediction()
								&& (cursor < mComposingText.size(ComposingText.LAYER1)))){

					mDisplayText.setSpan(SPAN_EXACT_BGCOLOR_HL, 0, cursor,
							Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
					highlightEnd = cursor;

				} else if (FIX_CURSOR_TEXT_END && mEngineState.isEisuKana()) {
					mDisplayText.setSpan(SPAN_EISUKANA_BGCOLOR_HL, 0, cursor,
							Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
					highlightEnd = cursor;

				} else if (layer == ComposingText.LAYER2) {
					highlightEnd = mComposingText.toString(layer, 0, 0).length();

					/* highlights the first segment */
					mDisplayText.setSpan(SPAN_CONVERT_BGCOLOR_HL, 0,
							highlightEnd,
							Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
				}

				if (FIX_CURSOR_TEXT_END && (highlightEnd != 0)) {
					/* highlights remaining text */
					mDisplayText.setSpan(SPAN_REMAIN_BGCOLOR_HL, highlightEnd,
							mComposingText.toString(layer).length(),
							Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

					/* text color in the highlight */
					mDisplayText.setSpan(SPAN_TEXTCOLOR, 0,
							mComposingText.toString(layer).length(),
							Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
				}
			}

			mDisplayText.setSpan(SPAN_UNDERLINE, 0, mDisplayText.length(),
					Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

			int displayCursor = mComposingText.toString(layer, 0, cursor - 1).length();
			if (FIX_CURSOR_TEXT_END) {
				displayCursor = (cursor == 0) ?  0 : 1;
			}
			/* update the composing text on the EditView */
			mInputConnection.setComposingText(mDisplayText, displayCursor);

		}
		if (indicateCandidates) {
			mCandidatesViewManager.setIndicateCandidateView();
		}
	}

	/**
	 * Update the candidates view.
	 */
	private void updateCandidateView() {
		switch (mTargetLayer) {
			case ComposingText.LAYER0:
			case ComposingText.LAYER1: /* prediction */
				if (mActionPrediction || mEngineState.isSymbolList() || mEngineState.isDocomoSymbolList() || mEngineState.isEisuKana()) {
					/* update the candidates view */
					if ((mComposingText.size(ComposingText.LAYER1) != 0)
							&& !mEngineState.isConvertState()) {

						mHandler.removeMessages(MSG_PREDICTION);
						if (mCandidatesViewManager.getCurrentView().isShown()) {
							mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION),
									mPredictionDelayMSShowingCandidate);
						} else {
							mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION),
									mPredictionDelayMS1st);
						}
					} else {
						mHandler.removeMessages(MSG_PREDICTION);
						updatePrediction();
					}
				} else {
					mHandler.removeMessages(MSG_PREDICTION);
					mCandidatesViewManager.clearCandidates();
					mDocomoEmojiCount = 0;
					resetPrediction();
				}
				break;
			case ComposingText.LAYER2: /* convert */
				if (mEngineState.isKanaDirect()) {
					mHandler.removeMessages(MSG_PREDICTION);
					mCandidatesViewManager.clearCandidates();
					mDocomoEmojiCount = 0;
					resetPrediction();
					mComposingText.setCursor(ComposingText.LAYER2, 1);
					mCandidatesViewManager.displayCandidates(mConverter);
					break;
				}
				if (mCommitCount == 0) {
					mHandler.removeMessages(MSG_PREDICTION);
					mConverter.convert(mComposingText);
				}

				final int candidates = mConverter.makeCandidateListOf(mCommitCount);

				if (candidates != 0) {
					mComposingText.setCursor(ComposingText.LAYER2, 1);
					mCandidatesViewManager.displayCandidates(mConverter);
				} else {
					mComposingText.setCursor(ComposingText.LAYER1, mComposingText.toString(ComposingText.LAYER1).length());
					mCandidatesViewManager.clearCandidates();
					mDocomoEmojiCount = 0;
					resetPrediction();
				}
				break;
			default:
				break;
		}
	}

	/**
	 * Commit the displaying composing text.
	 *
	 * @param learn  {@code true} to register the committed string to the learning dictionary.
	 * @return          IME's status after commit
	 */
	private int commitText(final boolean learn) {
		if (isEnglishPrediction()) {
			mComposingText.setCursor(ComposingText.LAYER1,
					mComposingText.size(ComposingText.LAYER1));
		}

		final int layer = mTargetLayer;
		final int cursor = mComposingText.getCursor(layer);
		if (cursor == 0) {
			return mStatus;
		}
		final String tmp = mComposingText.toString(layer, 0, cursor - 1);

		if (mConverter != null) {
			if (learn) {
				if (mEngineState.isRenbun()) {
					learnWord(0); /* select the top of the clauses */
				} else {
					if (mComposingText.size(ComposingText.LAYER1) != 0) {
						final String stroke = mComposingText.toString(ComposingText.LAYER1, 0, mComposingText.getCursor(layer) - 1);
						final WnnWord word = new WnnWord(tmp, stroke);

						learnWord(word);
					}
				}
			} else {
				breakSequence();
			}
		}
		return commitTextThroughInputConnection(tmp);
	}

	/**
	 * Commit all uncommitted words.
	 */
	private void commitAllText() {
		initCommitInfoForWatchCursor();
		if (mEngineState.isConvertState()) {
			commitConvertingText();
		} else {
			mComposingText.setCursor(ComposingText.LAYER1,
					mComposingText.size(ComposingText.LAYER1));
			mStatus = commitText(true);
		}
		checkCommitInfo();
	}

	/**
	 * Commit a word.
	 *
	 * @param word              A word to commit
	 * @return                  IME's status after commit
	 */
	private int commitText(final WnnWord word) {
		if (mConverter != null) {
			learnWord(word);
		}
		return commitTextThroughInputConnection(word.candidate);
	}

	/**
	 * Commit a string.
	 *
	 * @param str  A string to commit
	 */
	private void commitText(final String str) {
		mInputConnection.commitText(str, (FIX_CURSOR_TEXT_END ? 1 : str.length()));
		mPrevCommitText.append(str);
		mPrevCommitCount++;
		mEnableAutoDeleteSpace = true;
		updateViewStatusForPrediction(false, false, false);
	}

	/**
	 * Commit a string through {@link InputConnection}.
	 *
	 * @param string  A string to commit
	 * @return                  IME's status after commit
	 */
	private int commitTextThroughInputConnection(final String string) {
		int layer = mTargetLayer;

		mInputConnection.commitText(string, (FIX_CURSOR_TEXT_END ? 1 : string.length()));
		mPrevCommitText.append(string);
		mPrevCommitCount++;

		final int cursor = mComposingText.getCursor(layer);
		if (cursor > 0) {
			mComposingText.deleteStrSegment(layer, 0, mComposingText.getCursor(layer) - 1);
			mComposingText.setCursor(layer, mComposingText.size(layer));
		}
		mExactMatchMode = false;
		mCommitCount++;

		if ((layer == ComposingText.LAYER2) && (mComposingText.size(layer) == 0)) {
			layer = 1; /* for connected prediction */
		}

		final boolean commited = autoCommitEnglish();
		mEnableAutoDeleteSpace = true;

		if (layer == ComposingText.LAYER2) {
			final EngineState state = new EngineState();
			state.convertType = mEngineState.convertType;  // EngineState.CONVERT_TYPE_RENBUN;
			updateEngineState(state);
			updateViewStatus(layer, !commited, false, false);
		} else {
			if (mEngineState.isSymbolList() || mEngineState.isDocomoSymbolList()) {
				//
			} else {
				updateViewStatusForPrediction(!commited, false, false);
			}
		}

		if (mComposingText.size(ComposingText.LAYER0) == 0) {
			return STATUS_INIT;
		} else {
			return STATUS_INPUT_EDIT;
		}
	}

	/**
	 * Returns whether it is English prediction mode or not.
	 *
	 * @return  {@code true} if it is English prediction mode; otherwise, {@code false}.
	 */
	private boolean isEnglishPrediction() {
		return (mEngineState.isEnglish() && isEnableL2Converter());
	}

	/**
	 * Change the conversion engine and the letter converter(Romaji-to-Kana converter).
	 *
	 * @param mode  Engine's mode to be changed
	 * @see net.gorry.android.input.nicownng.NicoWnnGEvent.Mode
	 * @see net.gorry.android.input.nicownng.JAJP.DefaultSoftKeyboardJAJP
	 */
	private void changeEngineMode(final int mode) {
		EngineState state = new EngineState();
		switch (mode) {
			case ENGINE_MODE_OPT_TYPE_QWERTY:
				state.keyboard = EngineState.KEYBOARD_QWERTY;
				updateEngineState(state);
				clearCommitInfo();
				mDocomoEmojiCount = 0;
				resetPrediction();
				return;

			case ENGINE_MODE_OPT_TYPE_12KEY:
				state.keyboard = EngineState.KEYBOARD_12KEY;
				updateEngineState(state);
				clearCommitInfo();
				mDocomoEmojiCount = 0;
				resetPrediction();
				return;

			case ENGINE_MODE_EISU_KANA:
				if (mEngineState.isEisuKana()) {
					state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_NONE;
					updateEngineState(state);
					updateViewStatusForPrediction(true, true, true); /* prediction only */
				} else {
					startConvert(EngineState.CONVERT_TYPE_EISU_KANA);
				}
				mDocomoEmojiCount = 0;
				resetPrediction();
				return;

			case ENGINE_MODE_SYMBOL:
				if (mEnableSymbolList && !mDirectInputMode) {
					state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_SYMBOL;
					updateEngineState(state);
					updateViewStatusForPrediction(true, true, true);
				}
				mDocomoEmojiCount = 0;
				resetPrediction();
				return;

			case ENGINE_MODE_DOCOMOSYMBOL:
				if (mEnableSymbolList && !mDirectInputMode) {
					state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_DOCOMOSYMBOL00 + mDocomoEmojiCount;
					mDocomoEmojiCount = (mDocomoEmojiCount+1)&1;
					updateEngineState(state);
					updateViewStatusForPrediction(true, true, true);
				}
				return;

			default:
				break;
		}

		state = new EngineState();
		state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_NONE;
		updateEngineState(state);

		state = new EngineState();
		switch (mode) {
			case NicoWnnGEvent.Mode.DIRECT:
				/* Full/Half-width number or Full-width alphabet */
				mConverter = null;
				mPreConverter = null;
				break;

			case NicoWnnGEvent.Mode.NO_LV1_CONV:
				/* no Romaji-to-Kana conversion (=English prediction mode) */
				state.dictionarySet = EngineState.DICTIONARYSET_EN;
				updateEngineState(state);
				mConverter = mConverterEN;
				mPreConverter = null;
				break;

			case NicoWnnGEvent.Mode.NO_LV2_CONV:
				mConverter = null;
				mPreConverter = mPreConverterHiragana;
				break;

			case ENGINE_MODE_FULL_KATAKANA:
				mConverter = null;
				mPreConverter = mPreConverterFullKatakana;
				break;

			case ENGINE_MODE_HALF_KATAKANA:
				mConverter = null;
				mPreConverter = mPreConverterHalfKatakana;
				break;

			case ENGINE_MODE_FULL_KATAKANA_CONV:
				state.dictionarySet = EngineState.DICTIONARYSET_JP;
				updateEngineState(state);
				mConverter = mConverterJAJP;
				mPreConverter = mPreConverterFullKatakana;
				break;

			default:
				/* HIRAGANA input mode */
				state.dictionarySet = EngineState.DICTIONARYSET_JP;
				updateEngineState(state);
				mConverter = mConverterJAJP;
				mPreConverter = mPreConverterHiragana;
				break;
		}

		mPreConverterBack = mPreConverter;
		mConverterBack = mConverter;
	}

	/**
	 * Update the conversion engine's state.
	 *
	 * @param state  Engine's state to be updated
	 */
	private void updateEngineState(final EngineState state) {
		final EngineState myState = mEngineState;

		/* language */
		if ((state.dictionarySet != EngineState.INVALID)
				&& (myState.dictionarySet != state.dictionarySet)) {

			switch (state.dictionarySet) {
				case EngineState.DICTIONARYSET_EN:
					setDictionary(NicoWnnGEngineJAJP.DIC_LANG_EN);
					break;

				case EngineState.DICTIONARYSET_JP:
				default:
					setDictionary(NicoWnnGEngineJAJP.DIC_LANG_JP);
					break;
			}
			myState.dictionarySet = state.dictionarySet;
			breakSequence();

			/* update keyboard setting */
			if (state.keyboard == EngineState.INVALID) {
				state.keyboard = myState.keyboard;
			}
		}

		/* type of conversion */
		if ((state.convertType != EngineState.INVALID)
				&& (myState.convertType != state.convertType)) {

			switch (state.convertType) {
				case EngineState.CONVERT_TYPE_NONE:
				case EngineState.CONVERT_TYPE_PREDICT:
					setDictionary(mPrevDictionarySet);
					break;

				case EngineState.CONVERT_TYPE_EISU_KANA:
				case EngineState.CONVERT_TYPE_KANA_DIRECT:
					setDictionary(NicoWnnGEngineJAJP.DIC_LANG_JP_EISUKANA);
					break;

				case EngineState.CONVERT_TYPE_RENBUN:
				default:
					setDictionary(NicoWnnGEngineJAJP.DIC_LANG_JP);
					break;
			}
			myState.convertType = state.convertType;
		}

		/* temporary dictionary */
		if (state.temporaryMode != EngineState.INVALID) {

			switch (state.temporaryMode) {
				case EngineState.TEMPORARY_DICTIONARY_MODE_NONE:
					if (myState.temporaryMode != EngineState.TEMPORARY_DICTIONARY_MODE_NONE) {
						setDictionary(mPrevDictionarySet);
						mCurrentSymbol = -1;
						mPreConverter = mPreConverterBack;
						mConverter = mConverterBack;
						mDisableAutoCommitEnglishMask &= ~AUTO_COMMIT_ENGLISH_SYMBOL;
					}
					break;

				case EngineState.TEMPORARY_DICTIONARY_MODE_SYMBOL:
					if (mEnableSymbolListNonHalf) {
						switch (((DefaultSoftKeyboard)mInputViewManager).getKeyMode()) {
						case DefaultSoftKeyboard.KEYMODE_JA_HALF_ALPHABET:
						case DefaultSoftKeyboard.KEYMODE_JA_HALF_NUMBER:
							mConverterSymbolEngineBack.setDictionary(SymbolList.SYMBOL_ENGLISH);
							break;
						default:
							if (++mCurrentSymbol >= mConverterSymbolEngineBack.getJAJPSymbolNum()) {
								mCurrentSymbol = 0;
							}
							mConverterSymbolEngineBack.setDictionary(mCurrentSymbol);
							break;
						}
					} else {
						mConverterSymbolEngineBack.setDictionary(SymbolList.SYMBOL_ENGLISH);
					}
					mConverter = mConverterSymbolEngineBack;
					mDisableAutoCommitEnglishMask |= AUTO_COMMIT_ENGLISH_SYMBOL;
					breakSequence();
					break;

				case EngineState.TEMPORARY_DICTIONARY_MODE_DOCOMOSYMBOL00:
					mConverterSymbolEngineBack.setDictionary(SymbolList.SYMBOL_DOCOMO_EMOJI00);
					mConverter = mConverterSymbolEngineBack;
					mDisableAutoCommitEnglishMask |= AUTO_COMMIT_ENGLISH_SYMBOL;
					breakSequence();
					break;

				case EngineState.TEMPORARY_DICTIONARY_MODE_DOCOMOSYMBOL01:
					mConverterSymbolEngineBack.setDictionary(SymbolList.SYMBOL_DOCOMO_EMOJI01);
					mConverter = mConverterSymbolEngineBack;
					mDisableAutoCommitEnglishMask |= AUTO_COMMIT_ENGLISH_SYMBOL;
					breakSequence();
					break;

				default:
					break;
			}
			myState.temporaryMode = state.temporaryMode;
		}

		/* preference dictionary */
		if ((state.preferenceDictionary != EngineState.INVALID)
				&& (myState.preferenceDictionary != state.preferenceDictionary)) {

			myState.preferenceDictionary = state.preferenceDictionary;
			setDictionary(mPrevDictionarySet);
		}

		/* keyboard type */
		if (state.keyboard != EngineState.INVALID) {
			switch (state.keyboard) {
				case EngineState.KEYBOARD_12KEY:
					mConverterJAJP.setKeyboardType(NicoWnnGEngineJAJP.KEYBOARD_KEYPAD12);
					mConverterEN.setDictionary(NicoWnnGEngineEN.DICT_DEFAULT);
					break;

				case EngineState.KEYBOARD_QWERTY:
				default:
					mConverterJAJP.setKeyboardType(NicoWnnGEngineJAJP.KEYBOARD_QWERTY);
					if (mEnableSpellCorrection) {
						mConverterEN.setDictionary(NicoWnnGEngineEN.DICT_FOR_CORRECT_MISTYPE);
					} else {
						mConverterEN.setDictionary(NicoWnnGEngineEN.DICT_DEFAULT);
					}
					break;
			}
			myState.keyboard = state.keyboard;
		}
	}

	/**
	 * Set dictionaries to be used.
	 *
	 * @param mode  Definition of dictionaries
	 */
	private void setDictionary(final int mode) {
		int target = mode;
		switch (target) {

			case NicoWnnGEngineJAJP.DIC_LANG_JP:

				switch (mEngineState.preferenceDictionary) {
					case EngineState.PREFERENCE_DICTIONARY_PERSON_NAME:
						target = NicoWnnGEngineJAJP.DIC_LANG_JP_PERSON_NAME;
						break;
					case EngineState.PREFERENCE_DICTIONARY_POSTAL_ADDRESS:
						target = NicoWnnGEngineJAJP.DIC_LANG_JP_POSTAL_ADDRESS;
						break;
					default:
						break;
				}

				break;

			case NicoWnnGEngineJAJP.DIC_LANG_EN:

				switch (mEngineState.preferenceDictionary) {
					case EngineState.PREFERENCE_DICTIONARY_EMAIL_ADDRESS_URI:
						target = NicoWnnGEngineJAJP.DIC_LANG_EN_EMAIL_ADDRESS;
						break;
					default:
						break;
				}

				break;

			default:
				break;
		}

		switch (mode) {
			case NicoWnnGEngineJAJP.DIC_LANG_JP:
			case NicoWnnGEngineJAJP.DIC_LANG_EN:
				mPrevDictionarySet = mode;
				break;
			default:
				break;
		}

		mConverterJAJP.setDictionary(target);
	}

	/**
	 * Handle a toggle key input event.
	 *
	 * @param table  Table of toggle characters
	 */
	private void processSoftKeyboardToggleChar(final String[] table) {
		if (table == null) {
			return;
		}

		commitConvertingText();

		boolean toggled = false;
		if ((mStatus & ~STATUS_CANDIDATE_FULL) == STATUS_INPUT) {
			final int cursor = mComposingText.getCursor(ComposingText.LAYER1);
			if (cursor > 0) {
				final String prevChar = mComposingText.getStrSegment(ComposingText.LAYER1,
						cursor - 1).string;
				final String c = searchToggleCharacter(prevChar, table, false);
				if (c != null) {
					mComposingText.delete(ComposingText.LAYER1, false);
					appendStrSegment(new StrSegment(c));
					toggled = true;
				}
			}
		}

		if (!toggled) {
			if (!isEnableL2Converter()) {
				commitText(false);
			}

			String str = table[0];
			/* shift on */
			if (mAutoCaps
					&& isEnglishPrediction()
					&& (getShiftKeyState(getCurrentInputEditorInfo()) == 1)) {

				final char top = table[0].charAt(0);
				if (Character.isLowerCase(top)) {
					str = Character.toString(Character.toUpperCase(top));
				}
			}
			appendStrSegment(new StrSegment(str));
		}

		mStatus = STATUS_INPUT;

		// updateViewStatusForPrediction(true, true, true);
		updateViewStatusForPrediction(true, true, false);
	}

	/**
	 * Handle character input from the software keyboard without listing candidates.
	 *
	 * @param chars  The input character(s)
	 */
	private void processSoftKeyboardCodeWithoutConversion(final char[] chars) {
		if (chars == null) {
			return;
		}

		final ComposingText text = mComposingText;
		appendStrSegment(new StrSegment(chars));

		if (!isAlphabetLast(text.toString(ComposingText.LAYER1))) {
			/* commit if the input character is not alphabet */
			commitText(false);
		} else {
			final boolean completed = mPreConverter.convert(text);
			if (completed) {
				commitText(false);
			} else {
				mStatus = STATUS_INPUT;
				updateViewStatusForPrediction(true, true, true);
			}
		}
	}

	/**
	 * Handle character input from the software keyboard.
	 *
	 * @param chars   The input character(s)
	 */
	private void processSoftKeyboardCode(final char[] chars) {
		if (chars == null) {
			return;
		}

		if ((chars[0] == ' ') || (chars[0] == '\u3000' /* Full-width space */)) {
			if (mComposingText.size(0) == 0) {
				mCandidatesViewManager.clearCandidates();
				mDocomoEmojiCount = 0;
				resetPrediction();
				commitText(new String(chars));
				breakSequence();
			} else {
				if (isEnglishPrediction()) {
					initCommitInfoForWatchCursor();
					commitText(true);
					commitSpaceJustOne();
					checkCommitInfo();
				} else {
					if (mPreConverter != null) {
						mPreConverter.convert2(mComposingText);
					}
					startConvert(EngineState.CONVERT_TYPE_RENBUN);
				}
			}
			mEnableAutoDeleteSpace = false;
		} else {
			commitConvertingText();

			/* Auto-commit a word if it is English and Qwerty mode */
			boolean commit = false;
			if (isEnglishPrediction()
					&& (mEngineState.keyboard == EngineState.KEYBOARD_QWERTY)) {

				final Matcher m = mEnglishAutoCommitDelimiter.matcher(new String(chars));
				if (m.matches()) {
					commit = true;
				}
			}

			if (commit) {
				commitText(true);

				appendStrSegment(new StrSegment(chars));
				commitText(true);
			} else {
				appendStrSegment(new StrSegment(chars));
				if (mPreConverter != null) {
					mPreConverter.convert(mComposingText);
					mStatus = STATUS_INPUT;
				}
				// updateViewStatusForPrediction(true, true, true);
				updateViewStatusForPrediction(true, true, false);
			}
		}
	}

	/**
	 * Start consecutive clause conversion or EISU-KANA conversion mode.
	 *
	 * @param convertType               The conversion type({@code EngineState.CONVERT_TYPE_*})
	 */
	private void startConvert(final int convertType) {
		startConvert(convertType, false);
	}
	private void startConvert(final int convertType, boolean backward) {
		if (!isEnableL2Converter()) {
			return;
		}

		final int l0cursor = mComposingText.getCursor(ComposingText.LAYER0);
		final int l1cursor = mComposingText.getCursor(ComposingText.LAYER1);
		if (mEngineState.convertType != convertType) {
			/* adjust the cursor position */
			if (!mExactMatchMode) {
				if ((convertType == EngineState.CONVERT_TYPE_RENBUN) || (convertType == EngineState.CONVERT_TYPE_PREDICT)) {
					/* not specify */
					mComposingText.setCursor(ComposingText.LAYER1, 0);
				} else {
					if (mEngineState.isRenbun()||mEngineState.isPredict()) {
						/* EISU-KANA conversion specifying the position of the segment if previous mode is conversion mode */
						mExactMatchMode = true;
					} else {
						/* specify all range */
						mComposingText.setCursor(ComposingText.LAYER1,
								mComposingText.size(ComposingText.LAYER1));
					}
				}
			}

			if (convertType == EngineState.CONVERT_TYPE_RENBUN) {
				/* clears variables for the prediction */
				mExactMatchMode = false;
			}
			/* clears variables for the convert */
			mCommitCount = 0;

			int layer = ComposingText.LAYER2;
			if (convertType == EngineState.CONVERT_TYPE_EISU_KANA) {
				layer = ComposingText.LAYER1;
			}

			final EngineState state = new EngineState();
			state.convertType = convertType;
			updateEngineState(state);

			if ((convertType == EngineState.CONVERT_TYPE_PREDICT)) {
				WnnWord s = mCandidatesViewManager.getFirstWnnWordCandidate();
				if (backward) {
					s = mCandidatesViewManager.getPrevWnnWordCandidate();
				}
				if (s != null) {
					final int l1size = mComposingText.getStringLayer(ComposingText.LAYER1).size();
					final StrSegment[] ss = new StrSegment[(l1cursor == l1size) ? 1 : 2];
					ss[0] = new StrSegment(s.candidate, 0, (l1cursor>0 ? l1cursor-1 : 0));
					if (l1cursor != l1size) {
						String right = mComposingText.toString(ComposingText.LAYER1, l1cursor, l1size-1);
						ss[1] = new StrSegment(right, l1cursor, l1size-1);
					}
					final int l2size = mComposingText.getStringLayer(ComposingText.LAYER2).size();
					mComposingText.setCursor(ComposingText.LAYER2, l2size);
					final int l2cursor = mComposingText.getCursor(ComposingText.LAYER2);
					mComposingText.replaceStrSegment(ComposingText.LAYER2, ss, l2cursor);
					updateViewStatus(ComposingText.LAYER2, false, true, true);
					mComposingText.setCursorDirect(ComposingText.LAYER0, l0cursor);
					mComposingText.setCursorDirect(ComposingText.LAYER1, l1cursor);
					mComposingText.setCursorDirect(ComposingText.LAYER2, 1);
					mPrevCommitCount++;  // これがないと２文節以上の変換で中断されることがある
					return;
				}
				mPrevCommitCount++;  // これがないと変換中止したあとの変換再開で中断されることがある
			}
			updateViewStatus(layer, true, true, true);
		}
	}

	/**
	 * Auto commit a word in English (on half-width alphabet mode).
	 *
	 * @return  {@code true} if auto-committed; otherwise, {@code false}.
	 */
	private boolean autoCommitEnglish() {
		if (isEnglishPrediction() && (mDisableAutoCommitEnglishMask == AUTO_COMMIT_ENGLISH_ON)) {
			final CharSequence seq = mInputConnection.getTextBeforeCursor(2, 0);
			final Matcher m = mEnglishAutoCommitDelimiter.matcher(seq);
			if (m.matches()) {
				if ((seq.charAt(0) == ' ') && mEnableAutoDeleteSpace) {
					mInputConnection.deleteSurroundingText(2, 0);
					final CharSequence str = seq.subSequence(1, 2);
					mInputConnection.commitText(str, 1);
					mPrevCommitText.append(str);
					mPrevCommitCount++;
				}

				mHandler.removeMessages(MSG_PREDICTION);
				mCandidatesViewManager.clearCandidates();
				mDocomoEmojiCount = 0;
				resetPrediction();
				return true;
			}
		}
		return false;
	}

	/**
	 * Insert a white space if the previous character is not a white space.
	 */
	private void commitSpaceJustOne() {
		final CharSequence seq = mInputConnection.getTextBeforeCursor(1, 0);
		if (seq.charAt(0) != ' ') {
			commitText(" ");
		}
	}

	/**
	 * Get the shift key state from the editor.
	 *
	 * @param editor    The editor
	 * @return          State ID of the shift key (0:off, 1:on)
	 */
	protected int getShiftKeyState(final EditorInfo editor) {
		return (getCurrentInputConnection().getCursorCapsMode(editor.inputType) == 0) ? 0 : 1;
	}

	/**
	 * 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);
	}

	/**
	 * Memory a selected word.
	 *
	 * @param word  A selected word
	 */
	private void learnWord(final WnnWord word) {
		if (mEnableLearning && (word != null)) {
			mConverter.learn(word);
		}
	}

	/**
	 * Memory a clause which is generated by consecutive clause conversion.
	 *
	 * @param index  Index of a clause
	 */
	private void learnWord(final int index) {
		final ComposingText composingText = mComposingText;

		if (mEnableLearning && (composingText.size(ComposingText.LAYER2) > index)) {
			final StrSegment seg = composingText.getStrSegment(ComposingText.LAYER2, index);
			if (seg instanceof StrSegmentClause) {
				mConverter.learn(((StrSegmentClause)seg).clause);
			} else {
				final String stroke = composingText.toString(ComposingText.LAYER1, seg.from, seg.to);
				mConverter.learn(new WnnWord(seg.string, stroke));
			}
		}
	}

	/**
	 * Fits an editor info.
	 *
	 * @param preferences  The preference data.
	 * @param info              The editor info.
	 */
	private void fitInputType(final SharedPreferences pref, final EditorInfo info) {
		if (info.inputType == InputType.TYPE_NULL) {
			mDirectInputMode = true;
			return;
		}

		mEnableLearning   = pref.getBoolean("opt_enable_learning", true);
		//mEnablePrediction = pref.getBoolean("opt_prediction", true);
		mEnableSpellCorrection = pref.getBoolean("opt_spell_correction", true);
		mPredictionMode = predictionModeTable.get(pref.getString("opt_prediction_mode", "on_prediction"));
		mEnablePredictionAfterEnter = pref.getBoolean("opt_prediction_afterenter", false);
		resetPrediction();

		mDisableAutoCommitEnglishMask &= ~AUTO_COMMIT_ENGLISH_OFF;
		int preferenceDictionary = EngineState.PREFERENCE_DICTIONARY_NONE;
		mEnableConverter = true;
		mEnableSymbolList = true;
		mEnableSymbolListNonHalf = true;
		mAutoCaps = getOrientPrefBoolean(pref, "auto_caps", false);
		mFilter.filter = 0;
		//mEnableAutoInsertSpace = true;
		mEnableAutoHideKeyboard = false;

		mEnableAutoInsertSpace = getOrientPrefBoolean(pref, "is_skip_space", true);

		mInputViewFullScreenInLandscape = pref.getBoolean("fullscreen", false);

		mUseLeftRightKeyCandidateSelection = getOrientPrefBoolean(pref, "candidate_leftrightkey", true);

		switch (info.inputType & InputType.TYPE_MASK_CLASS) {
			case InputType.TYPE_CLASS_NUMBER:
			case InputType.TYPE_CLASS_DATETIME:
				mEnableConverter = false;
				break;

			case InputType.TYPE_CLASS_PHONE:
				mEnableSymbolList = false;
				mEnableConverter = false;
				break;

			case InputType.TYPE_CLASS_TEXT:

				switch (info.inputType & InputType.TYPE_MASK_VARIATION) {
					case InputType.TYPE_TEXT_VARIATION_PERSON_NAME:
						preferenceDictionary = EngineState.PREFERENCE_DICTIONARY_PERSON_NAME;
						break;

					case InputType.TYPE_TEXT_VARIATION_PASSWORD:
						mEnableLearning = false;
						mEnableConverter = false;
						mEnableSymbolListNonHalf = false;
						mFilter.filter = CandidateFilter.FILTER_NON_ASCII;
						mDisableAutoCommitEnglishMask |= AUTO_COMMIT_ENGLISH_OFF;
						mEnableAutoHideKeyboard = true;
						break;

					case InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS:
						//mFilter.filter = CandidateFilter.FILTER_NON_ASCII;
						//mEnableSymbolListNonHalf = false;
						//mEnableAutoInsertSpace = false;
						//mDisableAutoCommitEnglishMask |= AUTO_COMMIT_ENGLISH_OFF;
						//preferenceDictionary = EngineState.PREFERENCE_DICTIONARY_EMAIL_ADDRESS_URI;
						break;

					case InputType.TYPE_TEXT_VARIATION_URI:
						mEnableAutoInsertSpace = false;
						//mDisableAutoCommitEnglishMask |= AUTO_COMMIT_ENGLISH_OFF;
						//preferenceDictionary = EngineState.PREFERENCE_DICTIONARY_EMAIL_ADDRESS_URI;
						break;

					case InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS:
						preferenceDictionary = EngineState.PREFERENCE_DICTIONARY_POSTAL_ADDRESS;
						break;

					case InputType.TYPE_TEXT_VARIATION_PHONETIC:
						mEnableLearning = false;
						mEnableConverter = false;
						mEnableSymbolList = false;
						break;

					default:
						break;
				}
				break;

			default:
				break;
		}

		if (mFilter.filter == 0) {
			mConverterEN.setFilter(null);
			mConverterJAJP.setFilter(null);
		} else {
			mConverterEN.setFilter(mFilter);
			mConverterJAJP.setFilter(mFilter);
		}

		final EngineState state = new EngineState();
		state.preferenceDictionary = preferenceDictionary;
		state.convertType = EngineState.CONVERT_TYPE_NONE;
		state.keyboard = mEngineState.keyboard;
		updateEngineState(state);
		updateMetaKeyStateDisplay();

		checkTutorial(info.privateImeOptions);
	}

	/**
	 * Append a {@link StrSegment} to the composing text
	 * <br>
	 * If the length of the composing text exceeds
	 * {@code LIMIT_INPUT_NUMBER}, the appending operation is ignored.
	 *
	 * @param  str  Input segment
	 */
	private void appendStrSegment(final StrSegment str) {
		final ComposingText composingText = mComposingText;

		if (composingText.size(ComposingText.LAYER1) >= LIMIT_INPUT_NUMBER) {
			return; /* do nothing */
		}
		composingText.insertStrSegment(ComposingText.LAYER0, ComposingText.LAYER1, str);
		return;
	}

	/**
	 * Commit the consecutive clause conversion.
	 */
	private void commitConvertingText() {
		if (mEngineState.isConvertState()) {
			final int size = mComposingText.size(ComposingText.LAYER2);
			for (int i = 0; i < size; i++) {
				learnWord(i);
			}

			final String text = mComposingText.toString(ComposingText.LAYER2);
			mInputConnection.commitText(text, (FIX_CURSOR_TEXT_END ? 1 : text.length()));
			mPrevCommitText.append(text);
			mPrevCommitCount++;
			initializeScreen();
		}
	}

	/**
	 * Initialize the screen displayed by IME
	 */
	private void initializeScreen() {
		if (mComposingText.size(ComposingText.LAYER0) != 0) {
			mInputConnection.setComposingText("", 0);
		}
		mComposingText.clear();
		mExactMatchMode = false;
		mStatus = STATUS_INIT;
		mHandler.removeMessages(MSG_PREDICTION);
		final View candidateView = mCandidatesViewManager.getCurrentView();
		if ((candidateView != null) && candidateView.isShown()) {
			mCandidatesViewManager.clearCandidates();
			mDocomoEmojiCount = 0;
			resetPrediction();
		}
		mInputViewManager.onUpdateState(this);

		final EngineState state = new EngineState();
		state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_NONE;
		updateEngineState(state);
	}

	/**
	 * Whether the tail of the string is alphabet or not.
	 *
	 * @param  str      The string
	 * @return          {@code true} if the tail is alphabet; {@code false} if otherwise.
	 */
	private boolean isAlphabetLast(final String str) {
		final Matcher m = ENGLISH_CHARACTER_LAST.matcher(str);
		return m.matches();
	}

	/** @see net.gorry.android.input.nicownng.NicoWnnG#onFinishInput */
	@Override public void onFinishInput() {
		if (mInputConnection != null) {
			initializeScreen();
		}
		super.onFinishInput();
	}

	/**
	 * Check whether or not the converter is active.
	 *
	 * @return {@code true} if the converter is active.
	 */
	private boolean isEnableL2Converter() {
		if ((mConverter == null) || !mEnableConverter) {
			return false;
		}

		if (mEngineState.isEnglish() && !mActionPrediction) {
			return false;
		}

		return true;
	}

	/**
	 * Handling KeyEvent(KEYUP)
	 * <br>
	 * This method is called from {@link #onEvent()}.
	 *
	 * @param ev   An up key event
	 */
	private void onKeyUpEvent(final KeyEvent ev) {
		final int key = ev.getKeyCode();

		if (mRegisterLongPressHardwareKeyboardIS01MojiKey) {
			synchronized (this) {
				mHandlerLongPressHardwareKeyboardIS01MojiKey.removeCallbacks(mActionLongPressHardwareKeyboardIS01MojiKey);
				mRegisterLongPressHardwareKeyboardIS01MojiKey = false;
			}
			processKeyEvent(evHardwareKeyboardIS01MojiKey.keyEvent);
			return;
		}

		if (mRegisterLongPressHardwareKeyboardIS01EKaoKiKey) {
			synchronized (this) {
				mHandlerLongPressHardwareKeyboardIS01EKaoKiKey.removeCallbacks(mActionLongPressHardwareKeyboardIS01EKaoKiKey);
				mRegisterLongPressHardwareKeyboardIS01EKaoKiKey = false;
			}
			processKeyEvent(evHardwareKeyboardIS01EKaoKiKey.keyEvent);
			return;
		}

		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();
			}
		}
	}

	/**
	 * Initialize the committed text's information.
	 */
	private void initCommitInfoForWatchCursor() {
		if (!isEnableL2Converter()) {
			return;
		}

		mCommitStartCursor = mComposingStartCursor;
		mPrevCommitText.delete(0, mPrevCommitText.length());
	}

	/**
	 * Clear the commit text's info.
	 * @return {@code true}:cleared, {@code false}:has already cleared.
	 */
	private boolean clearCommitInfo() {
		if (mCommitStartCursor < 0) {
			return false;
		}

		mCommitStartCursor = -1;
		return true;
	}

	/**
	 * Verify the commit text.
	 */
	private void checkCommitInfo() {
		if (mCommitStartCursor < 0) {
			return;
		}

		final int composingLength = mComposingText.toString(mTargetLayer).length();
		CharSequence seq = mInputConnection.getTextBeforeCursor(mPrevCommitText.length() + composingLength, 0);
		if (seq != null) {
			seq = seq.subSequence(0, seq.length() - composingLength);
			if (!seq.equals(mPrevCommitText.toString())) {
				mPrevCommitCount = 0;
				clearCommitInfo();
			}
		}
	}

	/**
	 * Check and start the tutorial if it is the tutorial mode.
	 *
	 * @param privateImeOptions IME's options
	 */
	private void checkTutorial(final String privateImeOptions) {
		if (privateImeOptions == null) return;
		if (privateImeOptions.equals("com.android.setupwizard:ShowTutorial")) {
			if ((mTutorial == null) && mEnableTutorial) startTutorial();
		} else if (privateImeOptions.equals("com.android.setupwizard:HideTutorial")) {
			if (mTutorial != null) {
				if (mTutorial.close()) {
					mTutorial = null;
				}
			}
		}
	}

	/**
	 * Start the tutorial
	 */
	private void startTutorial() {
		final DefaultSoftKeyboardJAJP manager = (DefaultSoftKeyboardJAJP) mInputViewManager;
		manager.setDefaultKeyboard();
		if (mEngineState.keyboard == EngineState.KEYBOARD_QWERTY) {
			manager.changeKeyboardType(DefaultSoftKeyboard.KEYBOARD_12KEY);
		}

		final DefaultSoftKeyboardJAJP inputManager = ((DefaultSoftKeyboardJAJP) mInputViewManager);
		final View v = inputManager.getKeyboardView();
		v.setOnTouchListener(new View.OnTouchListener() {
			public boolean onTouch(final View v, final MotionEvent event) {
				return true;
			}});
		mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_TUTORIAL), 500);
	}

	/**
	 * Close the tutorial
	 */
	public void tutorialDone() {
		mTutorial = null;
	}

	/** @see NicoWnnG#close */
	@Override protected void close() {
		mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLOSE), 0);
	}

	/**
	 * Break the sequence of words.
	 */
	private void breakSequence() {
		mEnableAutoDeleteSpace = false;
		mConverterJAJP.breakSequence();
		mConverterEN.breakSequence();
	}

	public void changeKanaDirectPhase() {
		if (mPreConverter != null) {
			mPreConverter.convert2(mComposingText);
		}
		final KanaConverter conv = new KanaConverter();
		final ArrayList<StrSegment> ssIn = mComposingText.getStringLayer(ComposingText.LAYER1);
		final StringBuilder sb = new StringBuilder();
		for (int i=0; i<ssIn.size(); i++) {
			final String sIn = ssIn.get(i).string;
			final String sOut = conv.Hiragana2FullKatakana(sIn);
			sb.append(sOut);
		}
		mComposingText.setCursor(ComposingText.LAYER2, 0); // mComposingText.size(ComposingText.LAYER2));
		final StrSegment[] ss = new StrSegment[1];
		ss[0] = new StrSegment(sb.toString(), 0, sb.length()-1);
		mComposingText.setStrSegment(ComposingText.LAYER2, ss);
		startConvert(EngineState.CONVERT_TYPE_KANA_DIRECT);
	}

	public boolean isRenbun() {
		return mEngineState.isRenbun();
	}

	public boolean isPredict() {
		return mEngineState.isPredict();
	}

	public boolean candidatesViewManagerisShown() {
		return mCandidatesViewManager.getCurrentView().isShown();
	}
	
    public void setSpecialCandidateOnKana12KeyToggleMode(int sw) {
        mConverterJAJP.setSpecialCandidateOnKana12KeyToggleMode(sw);
    }
    
    public int getSpecialCandidateOnKana12KeyToggleMode() {
        return mConverterJAJP.getSpecialCandidateOnKana12KeyToggleMode();
    }

}
