/*
** Copyright 2010, The LimeIME Open Source Project
**
** Project Url: http://code.google.com/p/limeime/
** http://android.toload.net/
**
** This program is free software: you can redistribute it and/or modifyf
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.toload.main.hd;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Vibrator;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import net.toload.main.hd.keyboard.LIMEBaseKeyboard;
import net.toload.main.hd.keyboard.LIMEKeyboard;
import net.toload.main.hd.keyboard.LIMEKeyboardBaseView;
import net.toload.main.hd.keyboard.LIMEKeyboardView;
import net.toload.main.hd.keyboard.LIMEMetaKeyKeyListener;
import net.toload.main.hd.R;
import net.toload.main.hd.candidate.CandidateInInputViewContainer;
import net.toload.main.hd.candidate.CandidateView;
import net.toload.main.hd.candidate.CandidateViewContainer;
import net.toload.main.hd.global.ChineseSymbol;
import net.toload.main.hd.global.LIMEPreferenceManager;
import net.toload.main.hd.global.LIMEUtilities;
import net.toload.main.hd.global.Mapping;
import net.toload.main.hd.limesettings.LIMEPreference;
import net.toload.main.hd.limesettings.LIMEPreferenceHC;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
/**
* @author Art Hung
*/
@SuppressLint("DefaultLocale")
public class LIMEService extends InputMethodService implements
LIMEKeyboardBaseView.OnKeyboardActionListener {
static final boolean DEBUG = true;
static final String TAG = "LIMEService";
static final int KEYBOARD_SWITCH_CODE = -9;
static final int KEYBOARD_SWITCH_IM_CODE = -10;
private LIMEKeyboardView mInputView = null;
private CandidateInInputViewContainer mInputViewContainer = null;//Jeremy'12,5,3
private boolean mFixedCandidateViewOn; //Jeremy'12,5,3
private CandidateViewContainer mCandidateViewContainer;
private CandidateView mCandidateView = null;
private CandidateView mCandidateViewInInputView = null;
private CandidateView mCandidateViewStandAlone = null;
private CompletionInfo[] mCompletions;
private StringBuilder mComposing = new StringBuilder();
private boolean mPredictionOn;
private boolean mCompletionOn;
private boolean mCapsLock;
private boolean mAutoCap;
private boolean mHasShift;
private boolean mEnglishOnly;
private boolean mEnglishFlagShift;
private boolean mPersistentLanguageMode; //Jeremy '12,5,1
private int mShowArrowKeys; //Jeremy '12,5,22 force recreate keyboard if show arrow keys mode changes.
private int mSplitKeyboard; //Jeremy '12,5,26 force recreate keyboard if split keyboard settings changes; 6/19 changed to int
// if getMapping result has record then set to 'true'
public boolean hasMappingList = false;
//private boolean keydown = false;
private long mMetaState;
//private boolean mJustAccepted;
//private CharSequence mJustRevertedSeparator;
private int mImeOptions;
LIMEKeyboardSwitcher mKeyboardSwitcher;
private int mOrientation;
private int mHardkeyboardHidden;
private boolean mPredicting;
private Mapping selectedCandidate; //Jeremy '12,5,7 renamed from firstMathed
private int selectedIndex; //Jeremy '12,5,7 the index in resultList of selectedCandidate
private Mapping commitedCandidate; //Jeremy '12,5,7 renamed from tempMatched
private StringBuffer tempEnglishWord;
private List<Mapping> tempEnglishList;
private boolean hasPhysicalKeyPressed;
//private String mWordSeparators;
//private String misMatched; //Removed by Jeremy '13,1,10
private LinkedList<Mapping> mCandidateList; //Jeremy '12,5,7 renamed from templist
private Vibrator mVibrator;
private AudioManager mAudioManager;
private final float FX_VOLUME = 1.0f;
private boolean hasVibration = false;
private boolean hasSound = false;
private boolean hasNumberMapping = false;
private boolean hasSymbolMapping = false;
private boolean hasQuickSwitch = false;
// Hard Keyboad Shift + Space Status
private boolean hasShiftPress = false;
//private boolean hasShiftProcessed = false; // Jeremy '11,6.18
private boolean hasCtrlPress = false; // Jeremy '11,5,13
private boolean hasWinPress = false; // Jeremy '12,4,29 windows start key on stadard windows keyboard
//private boolean hasCtrlProcessed = false; // Jeremy '11,6.18
private boolean hasDistinctMultitouch;// Jeremy '11,8,3
private boolean hasShiftCombineKeyPressed = false; //Jeremy ,11,8, 3
private boolean hasMenuPress = false; // Jeremy '11,5,29
private boolean hasMenuProcessed = false; // Jeremy '11,5,29
//private boolean hasSearchPress = false; // Jeremy '11,5,29
//private boolean hasSearchProcessed = false; // Jeremy '11,5,29
private boolean hasEnterProcessed = false; // Jeremy '11,6.18
private boolean hasSpaceProcessed = false;
private boolean hasKeyProcessed = false; // Jeremy '11,8,15 for long pressed key
private int mLongPressKeyTimeout; //Jeremy '11,8, 15 read long press timeout from config
private boolean hasSymbolEntered = false; //Jeremy '11,5,24
// private boolean hasSpacePress = false;
// Hard Keyboad Shift + Space Status
//private boolean hasAltPress = false;
private String mIMActivatedState=""; // Jeremy '12,5,3, renamed from keyboardSelectedState
public String activeIM; //Jeremy '12,4,30 renamed from keyboardSelection
private List<String> activatedIMNameList; //Jeremy '12,4,30 renamed from keyboardList
private List<String> activatedIMShortNameList; //Jeremy '12,4,30 renamed from keyboardShortname
private List<String> activatedIMList; //jerem '12,4,30 reanmed from keybaordCodeList
private String currentSoftKeyboard = ""; //Jeremy '12,4,30 reanmed from keybaord_xml;
private String selkey;//Jeremy '12,7,5
// To keep key press time
//private long keyPressTime = 0;
// Keep keydown event
KeyEvent mKeydownEvent = null;
//private int previousKeyCode = 0;
//private final float moveLength = 15;
//private ISearchService SearchSrv = null;
private SearchServer SearchSrv = null;
static final int FREQUENCY_FOR_AUTO_ADD = 250;
// Weight added to a user picking a new word from the suggestion strip
static final int FREQUENCY_FOR_PICKED = 3;
// Auto Commmit Value
private int auto_commit = 0;
// Disable physical keyboard candidate words selection
private boolean disable_physical_selection = false;
// Replace Keycode.KEYCODE_CTRL_LEFT/RIGHT, ESC on android 3.x
// for backward compatibility of 2.x
static final int MY_KEYCODE_ESC = 111;
static final int MY_KEYCODE_CTRL_LEFT = 113;
static final int MY_KEYCODE_CTRL_RIGHT = 114;
static final int MY_KEYCODE_ENTER = 10;
static final int MY_KEYCODE_SPACE = 32;
static final int MY_KEYCODE_SWITCH_CHARSET = 95;
static final int MY_KEYCODE_WINDOWS_START = 117; //Jeremy '12,4,29 windows start key
private final String relatedSelkey = "!@#$%^&*()";
private String LDComposingBuffer=""; //Jeremy '11,7,30 for learning continuous typing phrases
private LIMEPreferenceManager mLIMEPref;
private boolean hasChineseSymbolCandidatesShown= false;
private boolean hasCandidatesShown = false;
/**
* Main initialization of the input method component. Be sure to call to
* super class.
*/
@Override
public void onCreate() {
if(DEBUG) Log.i(TAG, "OnCreate()");
super.onCreate();
SearchSrv = new SearchServer(this);
mEnglishOnly = false;
mEnglishFlagShift = false;
// Construct Preference Access Tool
mLIMEPref = new LIMEPreferenceManager(this);
mFixedCandidateViewOn = mLIMEPref.getFixedCandidateViewDisplay();
mVibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mLongPressKeyTimeout = getResources().getInteger(R.integer.config_long_press_key_timeout); // Jeremy '11,8,15 read longpress timeout from config resources.
// initial keyboard list
activatedIMNameList = new ArrayList<String>();
activatedIMList = new ArrayList<String>();
activatedIMShortNameList = new ArrayList<String>();
activeIM = mLIMEPref.getActiveIM();
buildActivatedIMList();
}
/**
* This is the point where you can do all of your UI initialization. It is
* called after creation and any configuration change.
*/
@Override
public void onInitializeInterface() {
if(DEBUG)
Log.i(TAG, "onInitializeInterface()");
initialViewAndSwitcher(false);
initCandidateView(); //Force the oncreatedcandidate to be called
mKeyboardSwitcher.makeKeyboards(true);
super.onInitializeInterface();
}
/**
* Called by the system when the device configuration changes while your activity is running.
*
*/
@Override
public void onConfigurationChanged(Configuration conf) {
if (DEBUG)
Log.i(TAG, "LIMEService:OnConfigurationChanged()");
//Jeremy '12,4,7 add hardkeyboard hidden configuration changed event and clear composing to avoid fc.
if (conf.orientation != mOrientation || conf.hardKeyboardHidden != mHardkeyboardHidden) {
//Jeremy '12,4,21 foce clear the composing buffer
clearComposing(true);
mOrientation = conf.orientation;
mHardkeyboardHidden = conf.hardKeyboardHidden;
}
initialViewAndSwitcher(true);
mKeyboardSwitcher.makeKeyboards(true);
super.onConfigurationChanged(conf);
}
/**
* Called by the framework when your view for creating input needs to be
* generated. This will be called the first time your input method is
* displayed, and every time it needs to be re-created such as due to a
* configuration change.
*/
@Override
public View onCreateInputView() {
if(DEBUG)
Log.i(TAG, "OnCreateInputView()");
initialViewAndSwitcher(true); //Jeremy '12,4,29. will do buildactivekeyboardlist in init startInput
if(mFixedCandidateViewOn){
if(DEBUG)
Log.i(TAG, "Fixed candiateView in on, return nInputViewContainer ");
return mInputViewContainer;
}else
return mInputView;
}
/**
* Create and return the view hierarchy used to show candidates.
* This will be called once, when the candidates are first displayed.
* You can return null to have no candidates view; the default implementation returns null.
*
*/
@Override
public View onCreateCandidatesView() {
if (DEBUG)
Log.i(TAG,"onCreateCandidatesView()");
mCandidateViewContainer = (CandidateViewContainer) getLayoutInflater().inflate(
R.layout.candidates, null);
mCandidateViewContainer.initViews();
mCandidateViewStandAlone = (CandidateView) mCandidateViewContainer.findViewById(R.id.candidates);
mCandidateViewStandAlone.setService(this);
if(!mFixedCandidateViewOn)
mCandidateView = mCandidateViewStandAlone;
return mCandidateViewContainer;
}
/**
* Override this to control when the input method should run in fullscreen mode.
* Jeremy '11,5,31
* Override fullscreen editing mode settings for larger screen (>1.4in)
*/
@Override
public boolean onEvaluateFullscreenMode() {
DisplayMetrics dm = getResources().getDisplayMetrics();
float displayHeight = dm.heightPixels;
// If the display is more than X inches high, don't go to fullscreen mode
float max = getResources().getDimension(R.dimen.max_height_for_fullscreen);
if(DEBUG)
Log.i(TAG, "onEvaluateFullScreenMode() DisplayHeight:"+displayHeight+" limit:" + max
+ "super.onEvaluateFullscreenMode():" + super.onEvaluateFullscreenMode());
//Jeremy '12,4,30 Turn off evaluation only for tablet and xhdpi phones (required horizontal >900pts)
if (displayHeight > max && this.getMaxWidth() > 900) {
return false;
} else {
return super.onEvaluateFullscreenMode();
}
}
/**
* This is called when the user is done editing a field. We can use this to
* reset our state.
*/
@Override
public void onFinishInput() {
if (DEBUG) {
Log.i(TAG ,"onFinishInput()");
}
super.onFinishInput();
if (mInputView != null) {
mInputView.closing();
}
try {
if(LDComposingBuffer.length()>0) { // Force interrupt the LD process
LDComposingBuffer = "";
SearchSrv.addLDPhrase(null, true);
}
// Jeremy '11,8,1 do postfinishinput in searchSrv (learn userdic and LDPhrase).
SearchSrv.postFinishInput();
} catch (RemoteException e) {
e.printStackTrace();
}
// Clear current composing text and candidates.
//Jeremy '12,5,21
finishComposing();
// -> 26.May.2011 by Art : Update keyboard list when user click the keyboard.
try {
mKeyboardSwitcher.setKeyboardList(SearchSrv.getKeyboardList());
mKeyboardSwitcher.setImList(SearchSrv.getImList());
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* add by Jeremy '12,4,21
* Send ic.finishComposingText upon composing is about to end
*/
private void finishComposing(){
if(DEBUG)
Log.i(TAG,"finishComposing()");
//Jeremy '11,8,14
if (mComposing != null && mComposing.length() > 0)
mComposing.setLength(0);
InputConnection ic = getCurrentInputConnection();
if(ic!=null) ic.finishComposingText();
selectedCandidate = null;
selectedIndex = 0;
if(mCandidateList!=null)
mCandidateList.clear();
mCandidateView.clear();
}
/**
* add by Jeremy '12,4,21
* clearComposing buffer upon composing is about to end
* add forceClearComposing parameter to control force clear the system composing buffer
*
*/
/**
* @param forceClearComposing
*/
private void clearComposing(boolean forceClearComposing){
if(DEBUG)
Log.i(TAG,"clearComposing()");
//Jeremy '11,8,14
if (mComposing != null && mComposing.length() > 0)
mComposing.setLength(0);
if(mCandidateList!=null)
mCandidateList.clear();
if(forceClearComposing){
InputConnection ic = getCurrentInputConnection();
if(ic!=null) ic.commitText("", 0);
}
selectedCandidate = null;
selectedIndex = 0;
clearSuggestions();
}
/**
* Clear suggestions or candidates in candidate view.
*/
private void clearSuggestions(){
if(mCandidateView !=null){
if(DEBUG)
Log.i(TAG, "clearSuggestions(): "
+ ", hasCandidatesShown:" + hasCandidatesShown);
if(!mEnglishOnly && mLIMEPref.getAutoChineseSymbol() //Jeremy '12,4,29 use mEnglishOnly instead of onIM
&& (hasCandidatesShown || mFixedCandidateViewOn) ){ // Change isCandiateShown() to hasCandiatesShown
mCandidateView.clear();
if(hasCandidatesShown) updateChineseSymbol(); // Jeremy '12.5,23 do not show chinesesymbol when init for fixed candidate view.
}else{
mCandidateView.clear();
hideCandidateView();
}
}
}
/**
* This is the main point where we do our initialization of the input method
* to begin operating on an application. At this point we have been bound to
* the client, and are now receiving all of the detailed information about
* the target of our edits.
*/
@Override
public void onStartInput(EditorInfo attribute, boolean restarting) {
if(DEBUG)
Log.i(TAG,"onStartInput()");
super.onStartInputView(attribute, restarting);
initOnStartInput(attribute, restarting);
}
@Override
public void onStartInputView(EditorInfo attribute, boolean restarting) {
if(DEBUG)
Log.i(TAG,"onStartInputView()");
super.onStartInputView(attribute, restarting);
initOnStartInput(attribute, restarting);
}
/**
* Initialization for IM and softkeybaords, and also choose wring lanaguage mode
* according the input attrubute in editorInfo
*/
private void initOnStartInput(EditorInfo attribute, boolean restarting) {
if (DEBUG)
Log.i(TAG, "initOnStartInput(): attribute.inputType & EditorInfo.TYPE_MASK_CLASS: "
+ (attribute.inputType & EditorInfo.TYPE_MASK_CLASS) );
if (mInputView == null) {
return;
}
//Jeremy '12,5,29 override the fixCanddiateMode setting in Landscape mode (in landscape mode the candidate bar is always not fixed).
boolean fixedCandiateMode = mLIMEPref.getFixedCandidateViewDisplay();
if(mOrientation == Configuration.ORIENTATION_LANDSCAPE )
fixedCandiateMode = false;
//jeremy '12,5,6 recreate inputview if fixedCandidateView setting is altered
if(mFixedCandidateViewOn != fixedCandiateMode) {
requestHideSelf(0);
mInputView.closing();
mFixedCandidateViewOn = fixedCandiateMode;
initialViewAndSwitcher(true);
if(mFixedCandidateViewOn){
if(DEBUG)
Log.i(TAG, "Fixed candiateView in on, return nInputViewContainer ");
setInputView(mInputViewContainer);
}else{
setInputView(mInputView);
if(DEBUG)
Log.i(TAG, "Fixed candiateView in off, return mInputView ");
}
}
hasPhysicalKeyPressed = false; //Jeremy '11,9,6 reset phsycalkeyflag
hasCandidatesShown = false;
// Reset the IM softkeyboard settings. Jeremy '11,6,19
try {
mKeyboardSwitcher.setImList(SearchSrv.getImList());
} catch (RemoteException e) {
e.printStackTrace();
}
mKeyboardSwitcher.makeKeyboards(
mShowArrowKeys != mLIMEPref.getShowArrowKeys() //Jeremy '12,5,22 recreate keyboard if the setting altered.
|| mSplitKeyboard != mLIMEPref.getSplitKeyboard()); //Jeremy '12,5,26 recreate keyboard if the setting altered.
loadSettings();
mImeOptions = attribute.imeOptions;
buildActivatedIMList(); //Jeremy '12,4,29 only this is required here instead of fully initialKeybaord
mPredictionOn = true;
mCompletionOn = false;
mCompletions = null;
mCapsLock = false;
mHasShift = false;
tempEnglishWord = new StringBuffer();
tempEnglishList = new LinkedList<Mapping>();
switch (attribute.inputType & EditorInfo.TYPE_MASK_CLASS) {
case EditorInfo.TYPE_CLASS_NUMBER: //0x02
case EditorInfo.TYPE_CLASS_DATETIME: //0x04
mEnglishOnly = true;
mKeyboardSwitcher.setKeyboardMode(activeIM, LIMEKeyboardSwitcher.MODE_TEXT, mImeOptions, false, true, false);
break;
case EditorInfo.TYPE_CLASS_PHONE: //0x03
mEnglishOnly = true;
mKeyboardSwitcher.setKeyboardMode(activeIM,
LIMEKeyboardSwitcher.MODE_PHONE, mImeOptions, false, false, false);
break;
case EditorInfo.TYPE_CLASS_TEXT: //0x01
// Make sure that passwords are not displayed in candidate view
int variation = attribute.inputType
& EditorInfo.TYPE_MASK_VARIATION;
if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
|| variation == EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME) {
//mAutoSpace = false;
} else {
//mAutoSpace = true;
}
if (variation == EditorInfo.TYPE_TEXT_VARIATION_FILTER) {
mPredictionOn = false;
}
if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) {
//disableAutoCorrect = true;
}
// If NO_SUGGESTIONS is set, don't do prediction.
if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) {
mPredictionOn = false;
//disableAutoCorrect = true;
}
// If it's not multiline and the autoCorrect flag is not set, then
// don't correct
if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0
&& (attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) == 0) {
//disableAutoCorrect = true;
}
if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
mPredictionOn = false;
mCompletionOn = isFullscreenMode();
}
// Switch keyboard here.
if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD
|| variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {
mPredictionOn = false;
//isModePassword = true;
mEnglishOnly = true;
//onIM = false;//Jeremy '12,4,29 use mEnglishOnly instead of onIM
mKeyboardSwitcher.setKeyboardMode(activeIM, LIMEKeyboardSwitcher.MODE_EMAIL,
mImeOptions, false, false, false);
break;
} else if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS) {
mEnglishOnly = true;
//onIM = false; //Jeremy '12,4,29 use mEnglishOnly instead of onIM
mPredictionOn = false;
mKeyboardSwitcher.setKeyboardMode(activeIM,
LIMEKeyboardSwitcher.MODE_EMAIL, mImeOptions, false, false, false);
break;
} else if (variation == EditorInfo.TYPE_TEXT_VARIATION_URI) {
mPredictionOn = false;
mEnglishOnly = true;
//onIM = false; //Jeremy '12,4,29 use mEnglishOnly instead of onIM
//isModeURL = true;
mKeyboardSwitcher.setKeyboardMode(activeIM,
LIMEKeyboardSwitcher.MODE_URL, mImeOptions, false, false, false);
break;
} else if (variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE) {
mEnglishOnly = false;
mKeyboardSwitcher.setKeyboardMode(activeIM, LIMEKeyboardSwitcher.MODE_IM, mImeOptions, true, false, false);
break;
} else {}
default:
if(mPersistentLanguageMode)
mEnglishOnly = mLIMEPref.getLanguageMode(); //Jeremy '12,4,30 restore lanaguage mode from preference.
if(mPersistentLanguageMode && mEnglishOnly){
mPredictionOn = true;
mEnglishOnly = true;
//onIM = false; //Jeremy '12,4,29 use mEnglishOnly instead of onIM
mKeyboardSwitcher.setKeyboardMode(activeIM, LIMEKeyboardSwitcher.MODE_TEXT,
mImeOptions, false, false, false);
} else{
mEnglishOnly = false;
initialIMKeyboard(); //'12,4,29 intial chinese IM keybaord
}
}
if(mEnglishOnly && !mPredictionOn) //Jeremy '12,5,20 Only hide candidateview when prediction mode is not on.
//Jeremy '12,5,6 clear internal composing buffer in forceHideCandiateView
forceHideCandidateView(); //Jeremy '12,5,6 zero the canidateView height to force hide it for eng/numeric keyboard
else{
clearComposing(false);//Jeremy '12,5,24 clear the suggesions and also restore the height of fixed candaiteview if it's hide before
//clearSuggestions(); // do this in clearcomposing already.
}
mPredicting = false;
updateShiftKeyState(getCurrentInputEditorInfo());
//initCandidateView(); //Force the oncreatedcandidate to be called
//clearComposing(false);
}
private void loadSettings() {
hasVibration = mLIMEPref.getVibrateOnKeyPressed();
hasSound = mLIMEPref.getSoundOnKeyPressed();
mPersistentLanguageMode = mLIMEPref.getPersistentLanguageMode();
activeIM = mLIMEPref.getActiveIM();
hasQuickSwitch = mLIMEPref.getSwitchEnglishModeHotKey();
mAutoCap = true;
mPersistentLanguageMode = mLIMEPref.getPersistentLanguageMode();
mShowArrowKeys = mLIMEPref.getShowArrowKeys();
mSplitKeyboard = mLIMEPref.getSplitKeyboard();
disable_physical_selection = mLIMEPref.getDisablePhysicalSelkey();
auto_commit = mLIMEPref.getAutoCommitValue();
currentSoftKeyboard = mKeyboardSwitcher.getImKeyboard(activeIM);
}
/**
* Deal with the editor reporting movement of its cursor.
*/
@Override
public void onUpdateSelection(int oldSelStart, int oldSelEnd,
int newSelStart, int newSelEnd, int candidatesStart,
int candidatesEnd) {
super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
candidatesStart, candidatesEnd);
if(DEBUG)
Log.i(TAG, "onUpdateSelection():oldSelStart" + oldSelStart
+" oldSelEnd:" + oldSelEnd
+" newSelStart:" + newSelStart + " newSelEnd:" + newSelEnd
+" candidatesStart:"+candidatesStart + " candidatesEnd:"+candidatesEnd );
InputConnection ic = getCurrentInputConnection();
if (mComposing.length() > 0
&& !(candidatesEnd == candidatesStart) //Jeremy '12,7,2 bug fixed on composition being clear after second word in chrome
&& candidatesStart >=0 && candidatesEnd >0 // in composing
) {
if(newSelStart < candidatesStart || newSelStart > candidatesEnd) { // cursor is moved before or after composing area
if(mCandidateList!=null) mCandidateList.clear();
//mCandidateView.clear();
hideCandidateView();
if (mComposing != null && mComposing.length() > 0){
mComposing.setLength(0);
if (ic != null)
ic.finishComposingText();
}
}else {
// Jeremy '12,5,23 Select the composing text and forbidded moving cursor within the composing text.
if (ic != null)
ic.setSelection(candidatesStart, candidatesEnd);
}
}
}
/**
* This tells us about completions that the editor has determined based on
* the current text in it. We want to use this in fullscreen mode to show
* the completions ourself, since the editor can not be seen in that
* situation.
*/
@Override
public void onDisplayCompletions(CompletionInfo[] completions) {
if (DEBUG)
Log.i(TAG, "onDisplayCompletions()");
if (mCompletionOn){
mCompletions = completions;
if(!mEnglishOnly){ //Jeremy '12,4,29 use mEnglishOnly instead of onIM
if(mComposing.length()==0) updateRelatedWord(false);
}
if(mEnglishOnly && !mPredictionOn) {
setSuggestions(buildCompletionList(), false, true,"");
}
}
}
/**
* This translates incoming hard key events in to edit operations on an
* InputConnection. It is only needed when using the PROCESS_HARD_KEYS
* option.
*/
private boolean translateKeyDown(int keyCode, KeyEvent event) {
// move to HandleCharacter '10, 3,26
// mMetaState = LIMEMetaKeyKeyListener.handleKeyDown(mMetaState,
// keyCode, event);
// mMetaState =
// LIMEMetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState);
hasPhysicalKeyPressed = true; // Jeremy '11,9,5
//hide softkeyboard. Jeremy '12,5,8
if(mInputView!=null && mInputView.isShown() && mLIMEPref.getAutoHideSoftKeyboard()){
mInputView.closing();
requestHideSelf(0);
}
if(DEBUG)
Log.i(TAG,"translateKeyDown() LIMEMetaKeyKeyListener.getMetaState(mMetaState) = "
+ Integer.toHexString( LIMEMetaKeyKeyListener.getMetaState(mMetaState))
+", event.getMetaState()" + Integer.toHexString( event.getMetaState()));
//Jeremy '12,5,28 after honeycomb use the metastate sent form KeyEvent to proces the shift/cap_lock etc...
//Jeremy '12,6,10 honeycomb starting from api 11 not 13.
int metaState;
if(android.os.Build.VERSION.SDK_INT > 10 && mLIMEPref.getPhysicalKeyboardType().equals("standard"))
metaState = event.getMetaState();
else
metaState = LIMEMetaKeyKeyListener.getMetaState(mMetaState);
int c = event.getUnicodeChar(metaState);
InputConnection ic = getCurrentInputConnection();
/// Jeremy '12,4,1 XPERIA Pro force translating special keys
if(mLIMEPref.getPhysicalKeyboardType().equals("xperiapro")) {
boolean isShift = LIMEMetaKeyKeyListener.getMetaState(mMetaState,
LIMEMetaKeyKeyListener.META_SHIFT_ON) > 0;
switch(keyCode){
case KeyEvent.KEYCODE_AT:
if(isShift) c ='/';
else c = '!';
break;
case KeyEvent.KEYCODE_APOSTROPHE:
if(isShift) c ='"';
else c = '\'';
break;
case KeyEvent.KEYCODE_GRAVE:
if(isShift) c ='~';
else c = '`';
break;
case KeyEvent.KEYCODE_COMMA:
if(isShift) c ='?';
else c = '.';
break;
case KeyEvent.KEYCODE_PERIOD:
if(isShift) c ='>';
else c = '@';
break;
}
}
if (c == 0 || ic == null) {
return false;
}
// Compact code by Jeremy '10, 3, 27
if (keyCode == 59) { // Translate shift as -1
c = -1;
}
if (c != -1 && (c & KeyCharacterMap.COMBINING_ACCENT) != 0) {
c = c & KeyCharacterMap.COMBINING_ACCENT_MASK;
}
onKey(c, null);
return true;
}
/**
* Physical KeyBoard Event Handler Use this to monitor key events being
* delivered to the application. We get first crack at them, and can either
* resume them or let them continue to the app.
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Clean code by jeremy '11,8,22
if (DEBUG)
Log.i(TAG, "OnKeyDown():keyCode:" + keyCode
+", hasMenuPress = " + hasMenuPress
+", hasdCtrlPress = " + hasCtrlPress
+", hasSHiftPress = " + hasShiftPress
+", hasWinPress = " + hasWinPress
+", event.getEventTime() - event.getDownTime()"+ ( event.getEventTime() - event.getDownTime())
+", event.getRepeatCount()" + event.getRepeatCount()
+", event.getMetaState()" + Integer.toHexString( event.getMetaState()));
mKeydownEvent = new KeyEvent(event);
// Record key pressed time and set key processed flags(key down, for physical keys)
//Jeremy '11,8,22 using getRepeatCount from event to set processed flags
if (event.getRepeatCount()==0){//!keydown) {
//keyPressTime = System.currentTimeMillis();
//keydown = true;
hasKeyProcessed = false;
hasMenuProcessed = false; // only do this on first keydown event
hasEnterProcessed = false;
hasSpaceProcessed = false;
hasSymbolEntered = false;
}
switch (keyCode) {
// Jeremy '11,5,29 Bypass search and menu combination keys.
case KeyEvent.KEYCODE_MENU:
hasMenuPress = true;
break;
// Add by Jeremy '10, 3, 29. DPAD selection on candidate view
case KeyEvent.KEYCODE_DPAD_RIGHT:
// Log.i("ART","select:"+1);
if (hasCandidatesShown){ //Replace isCandidateShown() with hasCandidatesShown by Jeremy '12,5,6
mCandidateView.selectNext();
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
// Log.i("ART","select:"+2);
if (hasCandidatesShown){ //Replace isCandidateShown() with hasCandidatesShown by Jeremy '12,5,6
mCandidateView.selectPrev();
return true;
}
break;
//Jeremy '11,8,28 for expanded canddiateviewi
case KeyEvent.KEYCODE_DPAD_UP:
// Log.i("ART","select:"+2);
if (hasCandidatesShown){ //Replace isCandidateShown() with hasCandidatesShown by Jeremy '12,5,6
mCandidateView.selectPrevRow();
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
// Log.i("ART","select:"+2);
if (hasCandidatesShown){ //Replace isCandidateShown() with hasCandidatesShown by Jeremy '12,5,6
mCandidateView.selectNextRow();
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_CENTER:
// Log.i("ART","select:"+3);
if (hasCandidatesShown){ //Replace isCandidateShown() with hasCandidatesShown by Jeremy '12,5,6
pickHighlightedCandidate();
return true;
}
break;
// Add by Jeremy '10,3,26, process metakey with
case KeyEvent.KEYCODE_SHIFT_LEFT:
case KeyEvent.KEYCODE_SHIFT_RIGHT:
hasShiftPress = true;
mMetaState = LIMEMetaKeyKeyListener.handleKeyDown(mMetaState, keyCode, event);
break;
case KeyEvent.KEYCODE_ALT_LEFT:
case KeyEvent.KEYCODE_ALT_RIGHT:
mMetaState = LIMEMetaKeyKeyListener.handleKeyDown(mMetaState, keyCode, event);
break;
case MY_KEYCODE_CTRL_LEFT:
case MY_KEYCODE_CTRL_RIGHT:
hasCtrlPress = true;
break;
case MY_KEYCODE_WINDOWS_START:
hasWinPress = true;
break;
case MY_KEYCODE_ESC:
case KeyEvent.KEYCODE_BACK:
// The InputMethodService already takes care of the back
// key for us, to dismiss the input method if it is shown.
// However, our keyboard could be showing a pop-up window
// that back should dismiss, so we first allow it to do that.
if (event.getRepeatCount() == 0) {
if(mInputView != null && mInputView.handleBack()){
Log.i(TAG,"KEYCODE_BACK mInputView handled the backed key");
return true;
}
//Jeremy '12,4,8 rewrite the logic here
else if(!mEnglishOnly && hasCandidatesShown //Replace isCandidateShown() with hasCandidatesShown by Jeremy '12,5,6
&& ( mComposing.length() > 0 ||
(selectedCandidate != null && selectedCandidate.isDictionary() &&
!hasChineseSymbolCandidatesShown )
) ){
if(DEBUG)
Log.i(TAG,"KEYCODE_BACK clearcomposing only.");
//Jeremy 12,4,21 -- need to check again
clearComposing(false);
return true;
}else if(!mEnglishOnly && hasCandidatesShown){ //Jeremy '12,6,13
hideCandidateView();
return true;
}
}
if(DEBUG)
Log.i(TAG,"KEYCODE_BACK return to super.");
break;
case KeyEvent.KEYCODE_DEL:
// Special handling of the delete key: if we currently are
// composing text for the user, we want to modify that instead
// of let the application to the delete itself.
hasPhysicalKeyPressed = true;
onKey(LIMEBaseKeyboard.KEYCODE_DELETE, null);
return true;
case KeyEvent.KEYCODE_ENTER:
// Let the underlying text editor always handle these, if return
// false from takeSelectedSuggestion().
// Process enter for candidate view selection in OnKeyUp() to block
// the real enter afterward.
// return false;
// Log.i("ART", "physical keyboard:"+ keyCode);
mMetaState = LIMEMetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState);
setInputConnectionMetaStateAsCurrentMetaKeyKeyListenerState();
if(!mEnglishOnly){ //Jeremy '12,4,29 use mEnglishOnly instead of onIM
if (hasCandidatesShown){ //Replace isCandidateShown() with hasCandidatesShown by Jeremy '12,5,6
// To block a real enter after suggestion selection. We have to
// return true in OnKeyUp();
if( pickHighlightedCandidate()){
hasEnterProcessed = true;
return true;
}else{
hideCandidateView();
break;
}
}
}else if(//mLIMEPref.getEnglishPrediction() &&
mPredictionOn && mLIMEPref.getEnglishPredictionOnPhysicalKeyboard()){
resetTempEnglishWord();
this.updateEnglishPrediction();
break;
}else //Jeremy '12',7,1 bug fixed on english mode enter not functioning in chrome
break;
/* case MY_KEYCODE_ESC:
//Jeremy '11,9,7 treat esc as back key
//Jeremy '11,8,14
clearComposing();
InputConnection ic=getCurrentInputConnection();
if(ic!=null) ic.commitText("", 0);
return true;*/
case KeyEvent.KEYCODE_SPACE:
hasQuickSwitch = mLIMEPref.getSwitchEnglishModeHotKey();
// If user enable Quick Switch Mode control then check if has
// Shift+Space combination
// '11,5,13 Jeremy added Ctrl-space switch chi/eng
// '11,6,18 Jeremy moved from on_KEY_UP
// '12,4,29 Jeremy add hasWinPress + space to switch chi/eng (earth key on zippy keyboard)
// '12,5,8 Jeremy add send the space key to onKey with translatekeydown for candidate processing if it's not switching chi/eng
if ((hasQuickSwitch && hasShiftPress) || hasCtrlPress || hasMenuPress || hasWinPress ){
if(!hasWinPress) this.switchChiEng(); //Jeremy '12,5,20 move hasWinPress to winstartkey in onkeyUp()
if(hasMenuPress) hasMenuProcessed = true;
hasSpaceProcessed =true;
return true;
} else
return translateKeyDown(keyCode, event);
case MY_KEYCODE_SWITCH_CHARSET: // experia pro earth key
case 1000: // milestone chi/eng key
switchChiEng();
break;
case KeyEvent.KEYCODE_SYM:
case KeyEvent.KEYCODE_AT:
//Jeremy '11,8,22 use begintime and eventtime in event to see if long-pressed or not.
if (!hasKeyProcessed
&& event.getRepeatCount() > 0
&& event.getEventTime() - event.getDownTime() > mLongPressKeyTimeout ) {
//&& System.currentTimeMillis() - keyPressTime > mLongPressKeyTimeout){
switchChiEng();
hasKeyProcessed = true;
}
return true;
case KeyEvent.KEYCODE_TAB: // Jeremy '12.6,22 Force bypassing tab processing to super if not on milestone 2 with alt on (alt+tab = ~ on milestone2)
if (!( LIMEMetaKeyKeyListener.getMetaState(mMetaState,
LIMEMetaKeyKeyListener.META_ALT_ON) > 0
&& mLIMEPref.getPhysicalKeyboardType().equals("milestone2") ) )
break;
default:
if(!(hasCtrlPress||hasMenuPress)){
if (translateKeyDown(keyCode, event)) {
if(DEBUG) Log.i(TAG,"Onkeydown():tranlatekeydown:true");
return true;
}
}
}
if((hasCtrlPress||hasMenuPress)&& !mEnglishOnly ) { //Jeremy '12,4,29 use mEnglishOnly instead of onIM
int primaryKey = event.getUnicodeChar(LIMEMetaKeyKeyListener.getMetaState(mMetaState));
char t = (char) primaryKey;
if (hasCtrlPress && //Only working with ctrl Jeremy '11,8,22
mCandidateList != null && mCandidateList.size() > 0
&& mCandidateView != null && hasCandidatesShown){
switch(keyCode){
case 8: this.pickCandidateManually(0);return true;
case 9: this.pickCandidateManually(1);return true;
case 10: this.pickCandidateManually(2);return true;
case 11: this.pickCandidateManually(3);return true;
case 12: this.pickCandidateManually(4);return true;
case 13: this.pickCandidateManually(5);return true;
case 14: this.pickCandidateManually(6);return true;
case 15: this.pickCandidateManually(7);return true;
case 16: this.pickCandidateManually(8);return true;
case 7: this.pickCandidateManually(9);return true;
}
}
if((mComposing == null || mComposing.length() == 0) ) {
// Jeremy '11,8,21. Ctrl-/ to fetch full-shaped chinese symbols in candidateview.
if(t=='/'){
if(hasMenuPress) hasMenuProcessed = true;
updateChineseSymbol();
return true;
}
// 27.May.2011 Art : when user click Ctrl + Symbol or number then send Chinese Symobl Characters
String s = ChineseSymbol.getSymbol(t);
if(s != null){
clearSuggestions();
getCurrentInputConnection().commitText(s, 0);
hasSymbolEntered = true;
if(hasMenuPress) hasMenuProcessed = true;
return true;
}
}
}
return super.onKeyDown(keyCode, event);
}
private void resetTempEnglishWord() {
tempEnglishWord.delete(0, tempEnglishWord.length());
tempEnglishList.clear();
}
private void setInputConnectionMetaStateAsCurrentMetaKeyKeyListenerState() {
InputConnection ic = getCurrentInputConnection();
if (ic != null) {
int clearStatesFlags = 0;
if (LIMEMetaKeyKeyListener.getMetaState(mMetaState,
LIMEMetaKeyKeyListener.META_ALT_ON) == 0)
clearStatesFlags += KeyEvent.META_ALT_ON;
if (LIMEMetaKeyKeyListener.getMetaState(mMetaState,
LIMEMetaKeyKeyListener.META_SHIFT_ON) == 0)
clearStatesFlags += KeyEvent.META_SHIFT_ON;
if (LIMEMetaKeyKeyListener.getMetaState(mMetaState,
LIMEMetaKeyKeyListener.META_SYM_ON) == 0)
clearStatesFlags += KeyEvent.META_SYM_ON;
ic.clearMetaKeyStates(clearStatesFlags);
}
}
/**
* Use this to monitor key events being delivered to the application. We get
* first crack at them, and can either resume them or let them continue to
* the app.
*/
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (DEBUG)
Log.i(TAG,"OnKeyUp():keyCode:" + keyCode
+";hasCtrlPress:" + hasCtrlPress
+";hasWinPress:" + hasWinPress
+", event.getEventTime() - event.getDownTime()"+ ( event.getEventTime() - event.getDownTime())
);
switch (keyCode) {
//Jeremy '11,5,29 Bypass search and menu keys.
// case KeyEvent.KEYCODE_SEARCH:
// hasSearchPress = false;
// if(hasSearchProcessed) return true;
// break;
case KeyEvent.KEYCODE_CAPS_LOCK:
// Modified by Art 20130607
// to switch the cap lock mode
toggleCapsLock();
case KeyEvent.KEYCODE_MENU:
hasMenuPress = false;
if(hasMenuProcessed) return true;
break;
// */------------------------------------------------------------------------
// Modified by Jeremy '10, 3,12
// keep track of alt state with mHasAlt.
// Modified '10, 3, 24 for bug fix and alt-lock implementation
case KeyEvent.KEYCODE_SHIFT_LEFT:
case KeyEvent.KEYCODE_SHIFT_RIGHT:
hasShiftPress = false;
mMetaState = LIMEMetaKeyKeyListener.handleKeyUp(mMetaState, keyCode, event);
// '11,8,28 Jeremy popup keyboard picker instaead of nextIM when onIM
// '11,5,14 Jeremy ctrl-shift switch to next available keyboard;
// '11,5,24 blocking switching if full-shape symbol
if (!hasSymbolEntered && !mEnglishOnly && (hasMenuPress || hasCtrlPress) ){ //Jeremy '12,4,29 use mEnglishOnly instead of onIM
//nextActiveKeyboard(true);
showIMPicker(); //Jeremy '11,8,28
if(hasMenuPress) {
hasMenuProcessed = true;
hasMenuPress = false;
}
mMetaState = LIMEMetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState);
setInputConnectionMetaStateAsCurrentMetaKeyKeyListenerState();
return true;
}
break;
case KeyEvent.KEYCODE_ALT_LEFT:
case KeyEvent.KEYCODE_ALT_RIGHT:
mMetaState = LIMEMetaKeyKeyListener.handleKeyUp(mMetaState, keyCode, event);
break;
case MY_KEYCODE_CTRL_LEFT:
case MY_KEYCODE_CTRL_RIGHT:
hasCtrlPress = false;
break;
case MY_KEYCODE_WINDOWS_START:
if(hasSpaceProcessed) //Jeremy '12,5,20 long press to show IM picker, switch chi/eng otherwise for the win+space or earth key on zippy
if(event.getEventTime() - event.getDownTime() > mLongPressKeyTimeout )
showIMPicker();
else
switchChiEng();
hasWinPress = false;
break;
case KeyEvent.KEYCODE_ENTER:
// Add by Jeremy '10, 3 ,29. Pick selected selection if candidates
// shown.
// Does not block real enter after select the suggestion. !! need
// fix here!!
// Let the underlying text editor always handle these, if return
// false from takeSelectedSuggestion().
if (hasEnterProcessed) {
return true;
}
// Jeremy '10, 4, 12 bug fix on repeated enter.
break;
case KeyEvent.KEYCODE_SYM:
case KeyEvent.KEYCODE_AT:
if(hasKeyProcessed){ //(keyPressTime != 0
//&& System.currentTimeMillis() - keyPressTime > 700) {
//switchChiEng(); // Jeremy '11,8,15 moved to onKeyDown()
return true;
} else if (LIMEMetaKeyKeyListener.getMetaState(mMetaState,
LIMEMetaKeyKeyListener.META_SHIFT_ON) > 0 && !mEnglishOnly //Jeremy '12,4,29 use mEnglishOnly instead of onIM
&& !mLIMEPref.getPhysicalKeyboardType().equals("xperiapro")) { // '12,4,1 Jeremy XPERIA Pro does not use this key as @
// alt-@ is conflict with symbol input thus altered to shift-@ Jeremy '11,8,15
// alt-@ switch to next active keyboard.
//nextActiveKeyboard(true);
showIMPicker(); //Jeremy '11,8,28
mMetaState = LIMEMetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState);
setInputConnectionMetaStateAsCurrentMetaKeyKeyListenerState();
return true;
// Long press physical @ key to swtich chn/eng
} else if (((mEnglishOnly && mPredictionOn) || (!mEnglishOnly))
&& translateKeyDown(keyCode, event)) {
return true;
} else {
translateKeyDown(keyCode, event);
super.onKeyDown(keyCode, mKeydownEvent);
}
break;
case KeyEvent.KEYCODE_SPACE:
//Jeremy move the chi/eng swithcing to on_KEY_UP '11,6,18
if(hasSpaceProcessed)
return true;
default:
}
// Update metakeystate of IC maintained by MetaKeyKeyListerner
//setInputConnectionMetaStateAsCurrentMetaKeyKeyListenerState(); moved to OnKey by jeremy '12,6,13
if (DEBUG)
Log.i(TAG,"OnKeyUp():keyCode:" + keyCode
+";hasCtrlPress:" + hasCtrlPress
+";hasWinPress:" + hasWinPress
+", event.getEventTime() - event.getDownTime()"+ ( event.getEventTime() - event.getDownTime())
+" call super.onKeyUp()"
);
return super.onKeyUp(keyCode, event);
}
/**
* Helper function to commit any text being composed in to the editor.
*/
private void commitTyped(InputConnection ic) {
if (DEBUG)
Log.i(TAG,"CommittedTyped()");
try {
if (mComposing.length() > 0 //denotes composing just finished
|| (selectedCandidate != null && selectedCandidate.isDictionary())) { //denotes related candidates are selected.
if (!mEnglishOnly) { //Jeremy '12,4,29 use mEnglishOnly instead of onIM
if (selectedCandidate != null && selectedCandidate.getWord() != null
&& !selectedCandidate.getWord().equals("")) {
int firstMatchedLength = 1;
if (selectedCandidate.getCode() == null
|| selectedCandidate.getCode().equals("")) {
firstMatchedLength = 1;
}
String wordToCommit = selectedCandidate.getWord();
if (selectedCandidate != null
&& selectedCandidate.getCode() != null
&& selectedCandidate.getWord() != null) {
if (selectedCandidate
.getCode()
.toLowerCase(Locale.US)
.equals(selectedCandidate.getWord()
.toLowerCase(Locale.US))) {
firstMatchedLength = 1;
}
}
if (DEBUG)
Log.i(TAG,"CommitedTyped() commited Length="
+ firstMatchedLength);
// Do hanConvert before commit
// '10, 4, 17 Jeremy
// inputConnection.setComposingText("", 1);
if(ic!=null) ic.commitText(
SearchSrv.hanConvert(wordToCommit),
firstMatchedLength);
// Art '30,Sep,2011 when show related then clear composing
if(currentSoftKeyboard.indexOf("wb") != -1){
clearComposing(true);
}
// Jeremy '11,7,28 for continuous typing (LD)
// Jeremy '12,6,2 get real commited code length from searchserver
boolean composingNotFinish = false;
//String commitedCode = selectedCandidate.getCode();
int commitedCodeLength = SearchSrv.getRealCodeLength(selectedIndex);
if(DEBUG)
Log.i(TAG, "commitedtype(): commitedCodeLength = " + commitedCodeLength);
if(mComposing.length() > selectedCandidate.getCode().length()){
composingNotFinish = true;
}
boolean shouldUpdateCandidates = false;
if(composingNotFinish){
if(LDComposingBuffer.length()==0){
//starting LD process
LDComposingBuffer = mComposing.toString();
if(DEBUG)
Log.i(TAG, "commitedtype():starting LD process, LDBuffer=" + LDComposingBuffer +
". just commited code= '" + selectedCandidate.getCode() + "'");
SearchSrv.addLDPhrase(selectedCandidate, false);
}else {//if(LDComposingBuffer.contains(mComposing.toString())){
//Continuous LD process
if(DEBUG)
Log.i(TAG, "commitedtype():Continuous LD process, LDBuffer='" + LDComposingBuffer +
"'. just commited code=" + selectedCandidate.getCode());
SearchSrv.addLDPhrase(selectedCandidate, false);
}
mComposing= mComposing.delete(0, commitedCodeLength);
if(DEBUG)
Log.i(TAG, "commitedtype(): trimmed mCopmosing = '" + mComposing + "', " +
"+ mCompsing.length = " + mComposing.length());
if(!mComposing.toString().equals(" ")){
if(mComposing.toString().startsWith(" "))
mComposing= mComposing.deleteCharAt(0);
if(DEBUG) Log.i(TAG, "commitedtype(): new mComposing:'" +mComposing + "'");
if(mComposing.length()>0){ //Jeremy '12,7,11 only fetch remaining composoing when length >0
if(ic!=null) ic.setComposingText(mComposing, 1);
//updateCandidates();
shouldUpdateCandidates = true;
//return;
}
}
} else {
if(LDComposingBuffer.length()>0 ){// && LDComposingBuffer.contains(mComposing.toString())){
//Ending continuous LD process (last of LD process)
if(DEBUG)
Log.i(TAG, "commitedtype():Ending LD process, LDBuffer=" + LDComposingBuffer +
". just commited code=" + selectedCandidate.getCode());
LDComposingBuffer = "";
SearchSrv.addLDPhrase(selectedCandidate, true);
}else if(LDComposingBuffer.length()>0){
//LD process interrupted.
if(DEBUG)
Log.i(TAG, "commitedtype():LD process interrupted, LDBuffer=" + LDComposingBuffer +
". just commited code=" + selectedCandidate.getCode());
LDComposingBuffer = "";
SearchSrv.addLDPhrase(null, true);
}
}
//Jeremy '13,1,10 do dict update and reverse query after updateRelatedword to shorten the time user see related candidates after select a candidate.
if(shouldUpdateCandidates){
updateCandidates();
}else{
commitedCandidate = selectedCandidate;
selectedCandidate = null;
clearComposing(false);
updateRelatedWord(false);
SearchSrv.addUserDictAndUpdateScore(commitedCandidate);
SearchSrv.rQuery(commitedCandidate.getWord());
}
/* Remove by jeremy '13,1,10 bacasue this section is equivalent to next else{} section.
} else if (selectedCandidate != null
&& selectedCandidate.getWord() != null
&& selectedCandidate.getWord().equals("")) { //Word="", commit the composing text.
if(ic!=null) ic.commitText(misMatched,
misMatched.length());
selectedCandidate = null;
*/
} else {
if(ic!=null) ic.commitText(mComposing,
mComposing.length());
}
} else { //English mode
if(ic!=null)
ic.commitText(mComposing, mComposing.length());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Helper to update the shift state of our keyboard based on the initial
* editor state.
*/
public void updateShiftKeyState(EditorInfo attr) {
if(DEBUG) Log.i(TAG, "updateShiftKeyState() " );
InputConnection ic = getCurrentInputConnection();
if (attr != null && mInputView != null
&& mKeyboardSwitcher.isAlphabetMode() && ic != null) {
int caps = 0;
EditorInfo ei = getCurrentInputEditorInfo();
if (mAutoCap && ei != null && ei.inputType != EditorInfo.TYPE_NULL) {
caps = ic.getCursorCapsMode(attr.inputType);
}
mInputView.setShifted(mCapsLock || caps != 0);
} else {
if (!mCapsLock && mHasShift) {
mKeyboardSwitcher.toggleShift();
mHasShift = false;
}
}
}
private boolean isValidLetter(int code) {
if (Character.isLetter(code)) {
return true;
} else {
return false;
}
}
private boolean isValidDigit(int code) {
if (Character.isDigit(code)) {
return true;
} else {
return false;
}
}
private boolean isValidSymbol(int code) {
String checkCode = String.valueOf((char) code);
// code has to < 256, a ascii character
if (code < 256 && checkCode.matches(".*?[^A-Z]")
&& checkCode.matches(".*?[^a-z]")
&& checkCode.matches(".*?[^0-9]") && code != 32) {
return true;
} else {
return false;
}
}
/**
* Helper to send a key down / key up pair to the current editor.
*/
private void keyDownUp(int keyEventCode, boolean sendToSelf) {
InputConnection ic=getCurrentInputConnection();
long eventTime = SystemClock.uptimeMillis();
KeyEvent downEvent = new KeyEvent(eventTime, eventTime,
KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0,
KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE);
KeyEvent upEvent = new KeyEvent(SystemClock.uptimeMillis(), eventTime,
KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0,
KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE);
if(sendToSelf) { //Jeremy '12,5,23 send to this.onKeyDown and onKeyUp if sendToSelf is true.
if(!this.onKeyDown(keyEventCode, downEvent) && ic!=null)
ic.sendKeyEvent(downEvent);
if(!this.onKeyUp(keyEventCode, upEvent) && ic!=null)
ic.sendKeyEvent(upEvent);;
}else if(ic!=null){
ic.sendKeyEvent(downEvent);
ic.sendKeyEvent(upEvent);
}
}
public void onKey(int primaryCode, int[] keyCodes) {
onKey(primaryCode, keyCodes,0,0);
}
public void onKey(int primaryCode, int[] keyCodes, int x, int y) {
if (DEBUG)
Log.i(TAG, "OnKey(): primaryCode:" + primaryCode
+ " hasShiftPress:" + hasShiftPress);
// Modified by Art
// This is to fixed the CapsLock issue on Physical keyboard
if(mCapsLock){
if(primaryCode >= 97 && primaryCode <= 122){
primaryCode -= 30;
}
}
// Adjust metakeystate on printed key pressed.
if(hasPhysicalKeyPressed){ //Jeremy '12,6,11 moved from handleCharacter()
mMetaState = LIMEMetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState);
setInputConnectionMetaStateAsCurrentMetaKeyKeyListenerState(); //Jeremy '12,6,13 moved from OnkeyUP by Jeremy '12,6,13
if(DEBUG)
Log.i(TAG,"onKey(): adjustMetaAfterKeypress()");
}
if (mLIMEPref.getEnglishPrediction()
&& primaryCode != LIMEBaseKeyboard.KEYCODE_DELETE) {
// Chcek if input character not valid English Character then reset
// temp english string
if (!Character.isLetter(primaryCode) && mEnglishOnly) {
//Jeremy '11,6,10. Select english sugestion with shift+123457890
if (hasPhysicalKeyPressed &&(mCandidateView != null && hasCandidatesShown)){ //Replace isCandidateShown() with hasCandidatesShown by Jeremy '12,5,6
if(handleSelkey(primaryCode, keyCodes)) {
return;
}
resetTempEnglishWord();
if(!hasCtrlPress) clearSuggestions(); //Jeremy '12,4,29 moved from resetcandidateBar
}
}
}
// Handle English/Lime Keyboard switch
if (mEnglishFlagShift == false
&& (primaryCode == LIMEBaseKeyboard.KEYCODE_SHIFT)) {
mEnglishFlagShift = true;
if (DEBUG) {
Log.i(TAG, "OnKey():mEnglishFlagShift:" + mEnglishFlagShift);
}
}
if (primaryCode == LIMEBaseKeyboard.KEYCODE_DELETE) {
handleBackspace();
} else if (primaryCode == LIMEBaseKeyboard.KEYCODE_SHIFT) {
if (DEBUG) Log.i(TAG, "OnKey():KEYCODE_SHIFT");
if(!(!hasPhysicalKeyPressed && hasDistinctMultitouch))
handleShift();
} else if (primaryCode == LIMEBaseKeyboard.KEYCODE_CANCEL) {// long press on options and shift
handleClose();
return;
// Jeremy '12,5,21 process the arrow keys on soft keyboard
} else if (primaryCode == LIMEBaseKeyboard.KEYCODE_UP) {
keyDownUp(KeyEvent.KEYCODE_DPAD_UP, hasCandidatesShown);
} else if (primaryCode == LIMEBaseKeyboard.KEYCODE_DOWN) {
keyDownUp(KeyEvent.KEYCODE_DPAD_DOWN, hasCandidatesShown);
} else if (primaryCode == LIMEBaseKeyboard.KEYCODE_RIGHT) {
keyDownUp(KeyEvent.KEYCODE_DPAD_RIGHT, hasCandidatesShown);
} else if (primaryCode == LIMEBaseKeyboard.KEYCODE_LEFT) {
keyDownUp(KeyEvent.KEYCODE_DPAD_LEFT, hasCandidatesShown);
} else if (primaryCode == LIMEKeyboardView.KEYCODE_OPTIONS) {
handleOptions();
} else if( primaryCode == LIMEKeyboardView.KEYCODE_SPACE_LONGPRESS) {
//showIMPicker();
startVoiceInput();
} else if (primaryCode == LIMEBaseKeyboard.KEYCODE_MODE_CHANGE && mInputView != null) {
switchKeyboard(primaryCode);
} else if (primaryCode == LIMEKeyboardView.KEYCODE_NEXT_IM){
switchToNextActivatedIM(true);
} else if (primaryCode == LIMEKeyboardView.KEYCODE_PREV_IM){
switchToNextActivatedIM(false);
} else if (primaryCode == KEYBOARD_SWITCH_CODE && mInputView != null) { //chi->eng
switchKeyboard(primaryCode);
// Jeremy '11,5,31 Rewrite softkeybaord enter/space and english sepeartor processing.
} else if (primaryCode == KEYBOARD_SWITCH_IM_CODE && mInputView != null) { //eng -> chi
switchKeyboard(primaryCode);
} else if ( //Jeremy '12,7,1 bug fixed on enter not functioning in english mode
((primaryCode== MY_KEYCODE_SPACE && !mEnglishOnly && !activeIM.equals("phonetic") )
//||(primaryCode== MY_KEYCODE_SPACE && !mEnglishOnly &&
// activeIM.equals("phonetic") //&& !mLIMEPref.getParameterBoolean("doLDPhonetic", true)
||(primaryCode== MY_KEYCODE_SPACE && !mEnglishOnly &&
activeIM.equals("phonetic") && (mComposing.toString().endsWith(" ")|| mComposing.length()==0 ))
|| primaryCode == MY_KEYCODE_ENTER) ){
if (hasCandidatesShown){ //Replace isCandidateShown() with hasCandidatesShown by Jeremy '12,5,6
if(!pickHighlightedCandidate()){//Jeremy '12,5,11 fixed for not sedning related.
if(mComposing.length() == 0)
hideCandidateView();
sendKeyChar((char)primaryCode);
}
}else{
sendKeyChar((char)primaryCode);
}
} else {
handleCharacter(primaryCode, keyCodes);
// Art 11, 9, 26 Check if need to auto commit composing
if(auto_commit > 0 && !mEnglishOnly){ //Jeremy '12,4,29 use mEnglishOnly instead of onIM
if(mComposing != null && mComposing.length() == auto_commit &&
currentSoftKeyboard != null && currentSoftKeyboard.indexOf("phone") != -1 ){
InputConnection ic = getCurrentInputConnection();
commitTyped(ic);
}
}
}
}
private AlertDialog mOptionsDialog;
// Contextual menu positions
private static final int POS_SETTINGS = 0;
private static final int POS_HANCONVERT = 1; //Jeremy '11,9,17
private static final int POS_KEYBOARD = 2;
private static final int POS_METHOD = 3;
private static final int POS_SPLIT_KEYBOARD= 4;
private static final int POS_VOICEINPUT = 5;
/**
* Add by Jeremy '10, 3, 24 for options menu in soft keyboard
*/
@TargetApi(11)
private void handleOptions() {
if(DEBUG)
Log.i(TAG, "handleOptions()");
AlertDialog.Builder builder = null;
if(android.os.Build.VERSION.SDK_INT < 11)
builder = new AlertDialog.Builder(this);
else
builder = new AlertDialog.Builder(this, R.style.LIMEHDTheme);
builder.setCancelable(true);
builder.setIcon(R.drawable.sym_keyboard_done);
builder.setNegativeButton(android.R.string.cancel, null);
builder.setTitle(getResources().getString(R.string.ime_name));
CharSequence itemSettings = getString(R.string.lime_setting_preference);
CharSequence hanConvert = getString(R.string.han_convert_option_list);
CharSequence itemSwitchIM = getString(R.string.keyboard_list);
CharSequence itemSwitchSytemIM = getString(R.string.input_method);
DisplayMetrics dm = getResources().getDisplayMetrics();
int displayWidth = dm.widthPixels;
int displayHeight = dm.heightPixels;
final boolean isLandScape = displayWidth >displayHeight;
CharSequence itemSplitKeyboard = getString(R.string.split_keyboard);
if((mSplitKeyboard == LIMEKeyboard.SPLIT_KEYBOARD_LANDSCAPD_ONLY && isLandScape)
|| mSplitKeyboard == LIMEKeyboard.SPLIT_KEYBOARD_ALWAYS)
itemSplitKeyboard = getString(R.string.merge_keyboard);
CharSequence[] options;
CharSequence itemVoiceInput = getString(R.string.voice_input);
final boolean hasSplitOption;
//Jeremy '12,5,27 do not show split/merge keyboard option if in landscape mode and show arrow keys is on
if(isLandScape && mShowArrowKeys > 0){
hasSplitOption = false;
if(android.os.Build.VERSION.SDK_INT > 13) {
options = new CharSequence[]
{itemSettings, hanConvert, itemSwitchIM, itemSwitchSytemIM, itemVoiceInput};
}else{
options = new CharSequence[]
{ itemSettings, hanConvert, itemSwitchIM, itemSwitchSytemIM};
}
}else{
hasSplitOption = true;
if(android.os.Build.VERSION.SDK_INT > 13){
options = new CharSequence[]
{itemSettings, hanConvert, itemSwitchIM, itemSwitchSytemIM, itemSplitKeyboard, itemVoiceInput};
}else{
options = new CharSequence[]
{ itemSettings, hanConvert, itemSwitchIM, itemSwitchSytemIM, itemSplitKeyboard};
}
}
builder.setItems( options, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface di, int position) {
di.dismiss();
switch (position) {
case POS_SETTINGS:
launchSettings();
break;
case POS_HANCONVERT: //Jeremy '11,9,17
showHanConvertPicker();
break;
case POS_KEYBOARD:
showIMPicker();
break;
case POS_METHOD:
((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).showInputMethodPicker();
break;
case POS_SPLIT_KEYBOARD: //Jeremy '12,5,27 new option to split keyboard; '12,6,9 add orientation consideration on split keyboard
if(hasSplitOption){
if(mSplitKeyboard == LIMEKeyboard.SPLIT_KEYBOARD_NEVER){
if(isLandScape)
mLIMEPref.setSplitKeyboard(LIMEKeyboard.SPLIT_KEYBOARD_LANDSCAPD_ONLY);
else
mLIMEPref.setSplitKeyboard(LIMEKeyboard.SPLIT_KEYBOARD_ALWAYS);
}else if(mSplitKeyboard == LIMEKeyboard.SPLIT_KEYBOARD_ALWAYS){
if(isLandScape)
mLIMEPref.setSplitKeyboard(LIMEKeyboard.SPLIT_KEYBOARD_NEVER);
else
mLIMEPref.setSplitKeyboard(LIMEKeyboard.SPLIT_KEYBOARD_LANDSCAPD_ONLY);
}else {// LIMEKeyboard.SPLIT_KEYBOARD_LANDSCAPD_ONLY
if(isLandScape)
mLIMEPref.setSplitKeyboard(LIMEKeyboard.SPLIT_KEYBOARD_NEVER);
else
mLIMEPref.setSplitKeyboard(LIMEKeyboard.SPLIT_KEYBOARD_ALWAYS);
}
handleClose();
mKeyboardSwitcher.makeKeyboards(true);
break;
}
case POS_VOICEINPUT:
startVoiceInput();
break;
}
}
});
mOptionsDialog = builder.create();
Window window = mOptionsDialog.getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
lp.token = mInputView.getWindowToken();
lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
window.setAttributes(lp);
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
mOptionsDialog.show();
}
private void launchSettings() {
handleClose();
Intent intent = new Intent();
if(android.os.Build.VERSION.SDK_INT < 11) //Jeremy '12,4,30 Add for deprecated preferenceActivity after API 11 (HC)
intent.setClass(LIMEService.this, LIMEPreference.class);
else
intent.setClass(LIMEService.this, LIMEPreferenceHC.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
private void switchToNextActivatedIM(boolean forward) { // forward: true, next IM; false prev. IM
if(DEBUG) Log.i(TAG, "switchToNextActivatedIM()");
buildActivatedIMList();
int i;
CharSequence activeIMName = "";
for (i = 0; i < activatedIMList.size(); i++) {
if (activeIM.equals(activatedIMList.get(i))) {
if (i == activatedIMList.size() - 1 && forward) {
activeIM = activatedIMList.get(0);
activeIMName = activatedIMNameList.get(0);
} else if(i == 0 && !forward){
activeIM = activatedIMList.get(activatedIMList.size() - 1);
activeIMName = activatedIMNameList.get(activatedIMList.size() - 1);
} else {
activeIM = activatedIMList.get(i + ((forward)?1:-1));
activeIMName = activatedIMNameList.get(i + ((forward)?1:-1));
}
break;
}
}
mLIMEPref.setActiveIM(activeIM);
//Jeremy '12,4,21 force clear when switch to next keybaord
clearComposing(true);
// cancel candidate view if it's shown
mEnglishOnly = false;
mLIMEPref.setLanguageMode(false);
//initialKeyboard();
initialIMKeyboard();
Toast.makeText(this, activeIMName, Toast.LENGTH_SHORT / 2).show();
try {
mKeyboardSwitcher.setKeyboardList(SearchSrv.getKeyboardList());
mKeyboardSwitcher.setImList(SearchSrv.getImList());
//mKeyboardSwitcher.clearKeyboards();
} catch (RemoteException e) {
e.printStackTrace();
}
// Update keyboard xml information
currentSoftKeyboard = mKeyboardSwitcher.getImKeyboard(activeIM);
}
private void buildActivatedIMList() {
CharSequence[] items = getResources().getStringArray(R.array.keyboard);
CharSequence[] shortNames = getResources().getStringArray(R.array.keyboardShortname);
CharSequence[] codes = getResources().getStringArray(
R.array.keyboard_codes);
String pIMActiveState = mLIMEPref.getIMActivatedState();
if(!(mIMActivatedState.length()>0 && mIMActivatedState.equals(pIMActiveState))) {
mIMActivatedState = pIMActiveState;
String[] s = pIMActiveState.toString().split(";");
activatedIMNameList.clear();
activatedIMList.clear();
activatedIMShortNameList.clear();
for (int i = 0; i < s.length; i++) {
int index = Integer.parseInt(s[i]);
if (index < items.length) {
activatedIMNameList.add(items[index].toString());
activatedIMShortNameList.add(shortNames[index].toString());
activatedIMList.add(codes[index].toString());
if(DEBUG)
Log.i(TAG, "buildActivatedIMList()(): buildActivatedIMList()["+index+"] = "
+ codes[index].toString() +" ;"+ shortNames[index].toString());
} else {
break;
}
}
}
if(DEBUG) Log.i(TAG, "currene active IM:" + activeIM);
// check if the selected keybaord is in active keybaord list.
boolean matched = false;
for (int i = 0; i < activatedIMList.size(); i++) {
if (activeIM.equals(activatedIMList.get(i))) {
if(DEBUG) Log.i(TAG, "buildActivatedIMList(): activatedIM["+i+"] matches current active IM: "+ activeIM);
matched = true;
break;
}
}
if (!matched && SearchSrv!=null ) {
// if the selected keyboard is not in the active keyboard list.
// set the keyboard to the first active keyboard
//if(DEBUG) Log.i(TAG, "currene keyboard is not in active list, reset to :" + keyboardListCodes.get(0));
activeIM = activatedIMList.get(0);
//initializeIMKeyboard();
}
}
/**
* Add by Jeremy '11,9,17 for han convert (tranditional <-> simplifed) options
*/
@TargetApi(11)
private void showHanConvertPicker() {
AlertDialog.Builder builder = null;
if(android.os.Build.VERSION.SDK_INT < 11)
builder = new AlertDialog.Builder(this);
else
builder = new AlertDialog.Builder(this, R.style.LIMEHDTheme);
builder.setCancelable(true);
builder.setIcon(R.drawable.sym_keyboard_done);
builder.setNegativeButton(android.R.string.cancel, null);
builder.setTitle(getResources().getString(R.string.han_convert_option_list));
CharSequence[] items = getResources().getStringArray(R.array.han_convert_options);
builder.setSingleChoiceItems(items, mLIMEPref.getHanCovertOption(),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface di, int position) {
di.dismiss();
handlHanConvertSelection(position);
}
});
mOptionsDialog = builder.create();
Window window = mOptionsDialog.getWindow();
if (!(window == null)) {
WindowManager.LayoutParams lp = window.getAttributes();
lp.token = mCandidateViewStandAlone.getWindowToken(); //Jeremy 12,5,4 it's always there
lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
window.setAttributes(lp);
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
}
mOptionsDialog.show();
}
private void handlHanConvertSelection(int position) {
mLIMEPref.setHanCovertOption(position);
}
/**
* Add by Jeremy '10, 3, 24 for IM picker menu in options menu
* renamed to showIMPicker from showKeybaordPicer to avoid confusion '12,3,40
*/
@TargetApi(11)
private void showIMPicker() {
if(DEBUG)
Log.i(TAG,"showIMPicker()");
buildActivatedIMList();
AlertDialog.Builder builder = null;
if(android.os.Build.VERSION.SDK_INT < 11)
builder = new AlertDialog.Builder(this);
else
builder = new AlertDialog.Builder(this, R.style.LIMEHDTheme);
builder.setCancelable(true);
builder.setIcon(R.drawable.sym_keyboard_done);
builder.setNegativeButton(android.R.string.cancel, null);
builder.setTitle(getResources().getString(R.string.keyboard_list));
CharSequence[] items = new CharSequence[activatedIMNameList.size()];// =
// getResources().getStringArray(R.array.keyboard);
int curKB = 0;
for (int i = 0; i < activatedIMNameList.size(); i++) {
items[i] = activatedIMNameList.get(i);
if (activeIM.equals(activatedIMList.get(i)))
curKB = i;
}
builder.setSingleChoiceItems(items, curKB,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface di, int position) {
di.dismiss();
handleIMSelection(position);
}
});
mOptionsDialog = builder.create();
Window window = mOptionsDialog.getWindow();
// Jeremy '10, 4, 12
// The IM is not initialialized. do nothing here if window=null.
if (!(window == null)) {
WindowManager.LayoutParams lp = window.getAttributes();
// Jeremy '11,8,28 Use candidate instead of mInputview because mInputView may not present when using physical keyboard
lp.token = mCandidateViewStandAlone.getWindowToken(); //always there Jeremy '12,5,4
lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
window.setAttributes(lp);
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
}
mOptionsDialog.show();
}
private void handleIMSelection(int position) {
if(DEBUG) Log.i(TAG, "handleIMSelection() position = " + position);
activeIM = activatedIMList.get(position);
mLIMEPref.setActiveIM(activeIM);
//spe.putString("keyboard_list", keyboardSelection);
//spe.commit();
//Jeremy '12,4,21 foce clear when switch to selected keybaord
if(!mEnglishOnly) clearComposing(true);
mEnglishOnly = false;//Jeremy '12,5,24 force to switch to Chinese mode if it's choosing in english mode.
initialIMKeyboard();
try {
mKeyboardSwitcher.setKeyboardList(SearchSrv.getKeyboardList());
mKeyboardSwitcher.setImList(SearchSrv.getImList());
//mKeyboardSwitcher.clearKeyboards();
// Update soft keybaord information
currentSoftKeyboard = mKeyboardSwitcher.getImKeyboard(activeIM);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void onText(CharSequence text) {
if (DEBUG)
Log.i(TAG, "OnText()");
InputConnection ic = getCurrentInputConnection();
if (ic == null)
return;
ic.beginBatchEdit();
if (mPredicting) {
commitTyped(ic);
//mJustRevertedSeparator = null;
} else if (!mEnglishOnly &&mComposing.length() > 0) { //Jeremy '12,4,29 use mEnglishOnly instead of onIM
pickHighlightedCandidate();
// commitTyped(ic);
}
ic.commitText(text, 1);
//ic.commitText(text, 0);
ic.endBatchEdit();
updateShiftKeyState(getCurrentInputEditorInfo());
}
private void updateCandidates() {
this.updateCandidates(false);
}
private void updateChineseSymbol(){
//ChineseSymbol chineseSym = new ChineseSymbol();
hasChineseSymbolCandidatesShown =true;
List<Mapping> list = ChineseSymbol.getChineseSymoblList();
if (list.size() > 0) {
// Setup sel key display if
String selkey = "1234567890";
if(disable_physical_selection && hasPhysicalKeyPressed){
selkey = "";
}
setSuggestions(list, hasPhysicalKeyPressed, true, selkey);
if(DEBUG) Log.i(TAG, "updateChineseSymbol():"
+ "templist.size:"+mCandidateList.size());
}
}
/**
* Update the list of available candidates from the current composing text.
* This will need to be filled in by however you are determining candidates.
*/
public void updateCandidates(boolean getAllRecords) {
if(DEBUG) Log.i(TAG,"updateCandidate():Update Candidate mComposing:"+ mComposing);
hasChineseSymbolCandidatesShown = false;
if (mComposing.length() > 0) {
//showCandidateView();
LinkedList<Mapping> list = new LinkedList<Mapping>();
try {
String keyString = mComposing.toString(), keynameString = "";
//Art '30,Sep,2011 restrict the length of composing text for Stroke5
if(currentSoftKeyboard.indexOf("wb") != -1){
if(keyString.length() > 5){
keyString = keyString.substring(0,5);
mComposing = new StringBuilder();
mComposing.append(keyString);
InputConnection ic = getCurrentInputConnection();
ic.setComposingText(keyString, 1);
}
}
list.addAll(SearchSrv.query(keyString, !hasPhysicalKeyPressed, getAllRecords));
//Jeremy '11,6,19 EZ and ETEN use "`" as IM Keys, and also custom may use "`".
if (list.size() > 0) {
// Setup sel key display if
if(disable_physical_selection && hasPhysicalKeyPressed){
selkey = "";
}else{
selkey=SearchSrv.getSelkey();
String mixedModeSelkey = "`";
if(hasSymbolMapping && !activeIM.equals("dayi")
&& ! (activeIM.equals("phonetic")
&& mLIMEPref.getPhoneticKeyboardType().equals("standard")) ){
mixedModeSelkey = " ";
}
int selkeyOption = mLIMEPref.getSelkeyOption();
if(selkeyOption ==1) selkey = mixedModeSelkey +selkey;
else if (selkeyOption ==2) selkey = mixedModeSelkey + " " +selkey;
}
setSuggestions(list, hasPhysicalKeyPressed, true, selkey);
if(DEBUG) Log.i(TAG, "updateCandidates(): display selkey:" + selkey
+ "list.size:"+list.size());
} else {
//Jermy '11,8,14
clearSuggestions();
}
// Show composing window if keyToKeyname got different string. Revised by Jeremy '11,6,4
if (SearchSrv.getTablename() != null ) {
keynameString = SearchSrv.keyToKeyname(keyString); //.toLowerCase(Locale.US)); moved to LimeDB
if (mCandidateView != null
&& !keynameString.toUpperCase(Locale.US).equals(keyString.toUpperCase(Locale.US))
//&& !keynameString.equals("")
&& !keynameString.trim().equals("")
) {
mCandidateView.setComposingText(keynameString);
}
}
} catch (NullPointerException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
} else
//Jermy '11,8,14
clearSuggestions();
}
/*
* Update English dictionary view
*/
private void updateEnglishPrediction() {
hasChineseSymbolCandidatesShown = false;
if (mPredictionOn && mLIMEPref.getEnglishPrediction()) {
try {
LinkedList<Mapping> list = new LinkedList<Mapping>();
Mapping empty = new Mapping();
empty.setWord("");
empty.setDictionary(true);
//Log.i("ART", "CACHE STRING -> " + tempEnglishWord.toString());
if (tempEnglishWord == null || tempEnglishWord.length() == 0) {
//list.add(empty);
//Jermy '11,8,14
clearSuggestions();
} else {
InputConnection ic = getCurrentInputConnection();
if(ic==null) return;
boolean after = false;
try {
char c = ic.getTextAfterCursor(1, 1).charAt(0);
if (!Character.isLetterOrDigit(c)) {
after = true;
}
} catch (StringIndexOutOfBoundsException e) {
after = true;
}
boolean matchedtemp = false;
if (tempEnglishWord.length() > 0) {
try {
if (tempEnglishWord.toString()
.equalsIgnoreCase(
ic.getTextBeforeCursor(
tempEnglishWord.toString()
.length(), 1)
.toString())) {
matchedtemp = true;
}
} catch (StringIndexOutOfBoundsException e) {
}
}
if (after || matchedtemp) {
tempEnglishList.clear();
Mapping temp = new Mapping();
temp.setWord(tempEnglishWord.toString());
temp.setDictionary(true);
List<Mapping> templist = SearchSrv
.queryDictionary(tempEnglishWord.toString());
if (templist.size() > 0) {
list.add(temp);
list.addAll(templist);
// Setup sel key display if
String selkey = "1234567890";
if(disable_physical_selection && hasPhysicalKeyPressed){
selkey = "";
}
setSuggestions(list, hasPhysicalKeyPressed
, true, selkey);
tempEnglishList.addAll(list);
} else {
//Jermy '11,8,14
clearSuggestions();
}
}
}
} catch (Exception e) {
Log.i("ART", "Error to update English predication");
}
}
}
/*
* Update dictionary view
*/
private void updateRelatedWord(boolean getAllRecords) {
if(DEBUG)
Log.i(TAG, "updateRelatedWord()" );
hasChineseSymbolCandidatesShown = false;
// Also use this to control whether need to display the english
// suggestions words.
// If there is no Temp Matched word exist then not to display dictionary
try {
// Modified by Jeremy '10, 4,1. getCode -> getWord
// if( tempMatched != null && tempMatched.getCode() != null &&
// !tempMatched.getCode().equals("")){
if (commitedCandidate != null && commitedCandidate.getWord() != null
&& !commitedCandidate.getWord().equals("")) {
LinkedList<Mapping> list = new LinkedList<Mapping>();
//Jeremy '11,8,9 Insert completion suggestions from application
//in front of related dictionary list in full-screen mode
if(mCompletionOn ){
list.addAll(buildCompletionList());
}
// Modified by Jeremy '10,3 ,12 for more specific related word
// -----------------------------------------------------------
if (commitedCandidate != null && hasMappingList) {
list.addAll( SearchSrv.queryUserDic(commitedCandidate.getWord(), getAllRecords));
}
// -----------------------------------------------------------
if (list.size() > 0) {
// Setup sel key display if
String selkey = "1234567890";
if(disable_physical_selection && hasPhysicalKeyPressed){
selkey = "";
}
setSuggestions(list, hasPhysicalKeyPressed && !isFullscreenMode()
, true, selkey);
} else {
commitedCandidate = null;
//Jermy '11,8,14
clearSuggestions();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private List<Mapping> buildCompletionList() {
LinkedList<Mapping> list = new LinkedList<Mapping>();
for (int i = 0; i < (mCompletions != null ? mCompletions.length : 0); i++) {
CompletionInfo ci = mCompletions[i];
if (ci != null) {
Mapping temp = new Mapping();
temp.setWord(ci.getText().toString());
temp.setCode("");
temp.setDictionary(true);
list.add(temp);
}
}
return list;
}
private void initCandidateView(){
if(DEBUG) Log.i(TAG,"initCandidateView()");
mHandler.post(mShowCandidateView);
mHandler.post(mHideCandidateView);
}
private void showCandidateView(){
if(DEBUG) Log.i(TAG,"showCandidateView()");
//if(mCandidateViewStandAlone == null ) return;
mHandler.post(mShowCandidateView);
}
private void hideCandidateView(){
if(DEBUG) Log.i(TAG,"hideCandidateView()");
if(mCandidateView!=null) mCandidateView.clear();
hasCandidatesShown = false;
hasChineseSymbolCandidatesShown = false;
if(mCandidateViewStandAlone==null || (mCandidateViewStandAlone!=null && !mCandidateViewStandAlone.isShown()))
return; // escape if mCandidateViewStandAlone is not created or it's not shown '12,5,6, Jeremy
mHandler.post(mHideCandidateView);
}
private void forceHideCandidateView(){
if (mComposing != null && mComposing.length() > 0)
mComposing.setLength(0);
// Reset templist
selectedCandidate = null;
selectedIndex = 0;
if(mCandidateList!=null)
mCandidateList.clear();
if(mFixedCandidateViewOn)
mCandidateView.forceHide();
else
hideCandidateView();
}
final Handler mHandler = new Handler();
// Create runnable for posting
final Runnable mShowCandidateView = new Runnable() {
public void run() {
if(DEBUG)
Log.i(TAG,"Runnable(): mShowCandidateView");
setCandidatesViewShown(true);
}
};
final Runnable mHideCandidateView = new Runnable() {
public void run() {
//Jeremy '12,4,24 moved fixedcandidate here
if(DEBUG)
Log.i(TAG,"Runnable(): mHideCandidateView");
setCandidatesViewShown(false);
}
};
public void setSuggestions(List<Mapping> suggestions, boolean showNumber,
boolean typedWordValid, String diplaySelkey){
if (suggestions != null && suggestions.size() > 0) {
if(DEBUG) Log.i(TAG, "setSuggestion():suggestions.size="+ suggestions.size()
+ " mFixedCandidateViewOn:" + mFixedCandidateViewOn
+ " hasPhysicalKeyPressed:" + hasPhysicalKeyPressed
);
hasCandidatesShown = true; //Jeremy '12,5,6 to replace deprecated isCandidateShown()
if(!mFixedCandidateViewOn || (mFixedCandidateViewOn && hasPhysicalKeyPressed ) ) //Jeremy '12,5,4
showCandidateView();
if((!mFixedCandidateViewOn||(mFixedCandidateViewOn && hasPhysicalKeyPressed) )
&& mCandidateView != mCandidateViewStandAlone )
mCandidateView = mCandidateViewStandAlone; //Jeremy '12,5,4 use standalone candidateView for physical keyboard (no soft keyboard shown)
hasMappingList = true;
if (mCandidateView != null) {
mCandidateList = (LinkedList<Mapping>) suggestions;
try {
if (suggestions.size() == 1) {
selectedCandidate = suggestions.get(0);
selectedIndex = 0;
} else if (suggestions.size() > 1 && !suggestions.get(0).getRelated()) {
selectedCandidate = suggestions.get(1);
selectedIndex = 1; //Jeremy '12,6,2 use getrelated to set default selectedCandidate
} else {
selectedCandidate = suggestions.get(0);
selectedIndex = 0;
}
} catch (Exception e) {
e.printStackTrace();
}
mCandidateView.setSuggestions(suggestions, showNumber,
typedWordValid, diplaySelkey);
if(DEBUG) Log.i(TAG, "setSuggestion(): templist.size: " + mCandidateList.size());
}
} else {
if(DEBUG) Log.i(TAG, "setSuggestion() with list=null");
hasMappingList = false;
//Jeremy '11,8,15
clearSuggestions();
}
}
private void handleBackspace() {
if(DEBUG)
Log.i(TAG, "handleBackspace()");
final int length = mComposing.length();
InputConnection ic=getCurrentInputConnection();
if (length > 1) {
mComposing.delete(length - 1, length);
if(ic!=null) ic.setComposingText(mComposing, 1);
updateCandidates();
} else if (length == 1) {
//Jeremy '12,4, 21 force clear the last characacter in composing
clearComposing(true);
//Jeremy '12,4,29 use mEnglishOnly instead of onIM
} else if(!mEnglishOnly // composing length == 0 after here
&& hasCandidatesShown // repalce isCandaiteShwon() with hasCandidatesShwn by Jeremy '12,5,6
//&& mLIMEPref.getAutoChineseSymbol()
&& !hasChineseSymbolCandidatesShown ){
clearComposing(false); //Jeremy '12,4,21 composing length 0, no need to force commit again.
} else if(!mEnglishOnly
//&& mCandidateView !=null && isCandidateShown()
&& hasCandidatesShown //Replace isCandidateShown() with hasCandidatesShown by Jeremy '12,5,6
//&& !mFixedCandidateViewOn //Jeremy '12,5,23 clear the chinese symbol list for arrow keys to do navigation inside document
){
hideCandidateView(); //Jeremy '11,9,8
} else {
//Jeremy '11,8,15
//clearSuggestions();
try {
if (mEnglishOnly && mLIMEPref.getEnglishPrediction()&& mPredictionOn
&& ( !hasPhysicalKeyPressed || mLIMEPref.getEnglishPredictionOnPhysicalKeyboard() )//mPredictionOnPhysicalKeyboard)
) {
if (tempEnglishWord != null && tempEnglishWord.length() > 0) {
tempEnglishWord.deleteCharAt(tempEnglishWord.length() - 1);
updateEnglishPrediction();
}
}
keyDownUp(KeyEvent.KEYCODE_DEL, false);
} catch (Exception e) {
Log.i(TAG,"->" + e);
}
}
}
public void setCandidatesViewShown(boolean shown) {
if(DEBUG)
Log.i(TAG,"setCandidateViewShown():" + shown);
if(shown)
super.setCandidatesViewShown(true);
else
super.setCandidatesViewShown(false);
if(DEBUG)
Log.i(TAG, "isCandidateViewShown:" + mCandidateViewStandAlone.isShown() );
}
private void handleShift() {
if(DEBUG) Log.i(TAG, "handleShift()");
if (mInputView == null) {
return;
}
if (mKeyboardSwitcher.isAlphabetMode()) {
// Alphabet keyboard
checkToggleCapsLock();
mInputView.setShifted(mCapsLock || !mInputView.isShifted());
mHasShift = mCapsLock || !mInputView.isShifted();
if (mHasShift) {
mKeyboardSwitcher.toggleShift();
}
} else {
if (mCapsLock) {
toggleCapsLock();
mHasShift = false;
} else if (mHasShift) {
toggleCapsLock();
mHasShift = true;
} else {
mKeyboardSwitcher.toggleShift();
mHasShift = mKeyboardSwitcher.isShifted();
}
}
}
/**
*
* Integrated all soft keyboards switching in this function.
*/
private void switchKeyboard(int primaryCode) {
if(DEBUG) Log.i(TAG,"switchKeyboard() primaryCode = " +primaryCode);
if (mCapsLock)
toggleCapsLock();
clearComposing(true);
hideCandidateView();
if (primaryCode == LIMEBaseKeyboard.KEYCODE_MODE_CHANGE) { //Symbol keyboard
mEnglishOnly = true;
mKeyboardSwitcher.toggleSymbols();
} else if (primaryCode == KEYBOARD_SWITCH_CODE) { //Chi --> Eng
mEnglishOnly = true;
mLIMEPref.setLanguageMode(true);
mKeyboardSwitcher.toggleChinese();
} else if(primaryCode == KEYBOARD_SWITCH_IM_CODE){ //Eng --> Chi moved from SwitchKeyboardIM by Jeremy '12,4,29
mEnglishOnly = false;
mLIMEPref.setLanguageMode(false);
initialIMKeyboard();
}
mHasShift = false;
updateShiftKeyState(getCurrentInputEditorInfo());
// Update keyboard xml information
currentSoftKeyboard = mKeyboardSwitcher.getImKeyboard(activeIM);
}
/**
* For physical keybaord to switch between chinese and english mode.
*/
private void switchChiEng() {
if(DEBUG)
Log.i(TAG,"switchChiEng(): mEnglishOnly:" + mEnglishOnly);
//Jeremy '12,4,21 force clear before switching chi/eng
clearComposing(true);
mKeyboardSwitcher.toggleChinese();
mEnglishOnly = !mKeyboardSwitcher.isChinese();
mLIMEPref.setLanguageMode(mEnglishOnly);
if(DEBUG)
Log.i(TAG, "switchChiEng(): mEnglishOnly updated as " + mEnglishOnly);
if (mEnglishOnly) {
Toast.makeText(this, R.string.typing_mode_english,
Toast.LENGTH_SHORT / 2).show();
} else {
Toast.makeText(this, R.string.typing_mode_mixed,
Toast.LENGTH_SHORT / 2).show();
}
clearSuggestions(); //Jeremy '11,9,5
}
private void initialViewAndSwitcher(boolean forceRecreate) {
if(DEBUG)
Log.i(TAG, "initialViewAndSwitcher()");
if(mFixedCandidateViewOn){ //Have candidateview in InputView
//Create inputView if it's null
if (mInputViewContainer == null || forceRecreate //|| mLIMEPref.getFixedCandidateViewDisplay()!=mFixedCandidateViewOn
) {
mInputViewContainer = (CandidateInInputViewContainer) getLayoutInflater().inflate(
R.layout.inputcandidate, null);
mInputView = (LIMEKeyboardView) mInputViewContainer.findViewById(R.id.keyboard);
mInputView.setOnKeyboardActionListener(this);
hasDistinctMultitouch = mInputView.hasDistinctMultitouch();
mInputViewContainer.initViews();
mCandidateViewInInputView = (CandidateView) mInputViewContainer.findViewById(R.id.candidatesView);
mCandidateViewInInputView.setService(this);
}
mCandidateView = mCandidateViewInInputView;
}else{
if (mInputView == null ||forceRecreate //|| mLIMEPref.getFixedCandidateViewDisplay()!=mFixedCandidateViewOn
) {
mInputView = (LIMEKeyboardView) getLayoutInflater().inflate(
R.layout.input, null);
mInputView.setOnKeyboardActionListener(this);
}
mCandidateView = mCandidateViewStandAlone;
}
// Checkif mKeyboardSwitcher == null
if (mKeyboardSwitcher == null) {
mKeyboardSwitcher = new LIMEKeyboardSwitcher(this);
//makeyboardSwitcher.setInputView(mInputView);
}
mKeyboardSwitcher.setInputView(mInputView);
buildActivatedIMList();
mKeyboardSwitcher.setActivatedIMList(activatedIMList, activatedIMNameList, activatedIMShortNameList);
if (mKeyboardSwitcher.getKeyboardSize() == 0 && SearchSrv != null) {
try {
mKeyboardSwitcher.setKeyboardList(SearchSrv.getKeyboardList());
mKeyboardSwitcher.setImList(SearchSrv.getImList());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
/**
* For initializing Chinese IM and corresponding soft keyboards.
*/
private void initialIMKeyboard(){
if(DEBUG)
Log.i(TAG,"initalizeIMKeyboard(): keyboardSelection:" + activeIM);
//mEnglishOnly = false;
//super.setCandidatesViewShown(false);
if (activeIM.equals("custom")) {
mKeyboardSwitcher.setKeyboardMode(activeIM,
LIMEKeyboardSwitcher.MODE_TEXT, mImeOptions, true, false, false);
hasNumberMapping = mLIMEPref.getAllowNumberMapping();
hasSymbolMapping = mLIMEPref.getAllowSymoblMapping();
}else if(activeIM.equals("cj")|| activeIM.equals("scj") || activeIM.equals("cj5") || activeIM.equals("ecj") ){
mKeyboardSwitcher.setKeyboardMode(activeIM,
LIMEKeyboardSwitcher.MODE_TEXT, mImeOptions, true, false, false);
hasNumberMapping = false;
hasSymbolMapping = false;
}else if (activeIM.equals("phonetic")){
mKeyboardSwitcher.setKeyboardMode(activeIM,
LIMEKeyboardSwitcher.MODE_TEXT, mImeOptions, true, false, false);
//Jeremy '11,6,18 ETEN 26 has no number mapping
boolean standardPhonetic = !(mLIMEPref.getPhoneticKeyboardType().equals("eten26")
||mLIMEPref.getPhoneticKeyboardType().equals("hsu"));
hasNumberMapping = standardPhonetic;
hasSymbolMapping = standardPhonetic;
}else if(activeIM.equals("ez")|| activeIM.equals("dayi")) {
mKeyboardSwitcher.setKeyboardMode(activeIM,
LIMEKeyboardSwitcher.MODE_TEXT, mImeOptions, true, false, false);
hasNumberMapping = true;
hasSymbolMapping = true;
}else if (activeIM.equals("array10")) {
hasNumberMapping = true;
hasSymbolMapping = false;
mKeyboardSwitcher.setKeyboardMode(activeIM,
LIMEKeyboardSwitcher.MODE_TEXT, mImeOptions, true, false, false);
}else if (activeIM.equals("array")) {
hasNumberMapping = true; //Jeremy '12,4,28 array 30 actually use number combination keys to enter symbols
hasSymbolMapping = true;
mKeyboardSwitcher.setKeyboardMode(activeIM,
LIMEKeyboardSwitcher.MODE_TEXT, mImeOptions, true, false, false);
}else if (activeIM.equals("wb")) {
hasNumberMapping = false;
hasSymbolMapping = true;
mKeyboardSwitcher.setKeyboardMode(activeIM,
LIMEKeyboardSwitcher.MODE_TEXT, mImeOptions, true, false, false);
}else if (activeIM.equals("hs")) {
hasNumberMapping = true;
hasSymbolMapping = true;
mKeyboardSwitcher.setKeyboardMode(activeIM,
LIMEKeyboardSwitcher.MODE_TEXT, mImeOptions, true, false, false);
}else if (activeIM.equals("pinyin")) {
hasNumberMapping = true;
hasSymbolMapping = false;
mKeyboardSwitcher.setKeyboardMode(activeIM,
LIMEKeyboardSwitcher.MODE_TEXT, mImeOptions, true, false, false);
}else {
mKeyboardSwitcher.setKeyboardMode(activeIM,
LIMEKeyboardSwitcher.MODE_TEXT, mImeOptions, true, false, false);
}
//Jeremy '11,9,3 for phone numeric key direct input on chacha
if(mLIMEPref.getPhysicalKeyboardType().equals("chacha")) hasNumberMapping = false;
String tablename = activeIM;
if (tablename.equals("custom") || tablename.equals("phone")) {
tablename = "custom";
}
//Jeremy '11,6,10 pass hasnumbermapping and hassymbolmapping to searchservice for selkey validation.
if(DEBUG)
Log.i(TAG, "switchKeyboard() current keyboard:" +
tablename+" hasnumbermapping:" +hasNumberMapping + " hasSymbolMapping:" + hasSymbolMapping);
SearchSrv.setTablename(tablename, hasNumberMapping, hasSymbolMapping);
}
private boolean handleSelkey(int primaryCode, int[] keyCodes){
if(DEBUG)
Log.i(TAG, "handleSelKey()");
// Jeremy '12,4,1 only do selkey on starndard keyboard
// Check if disable physical key option is open
if((disable_physical_selection && hasPhysicalKeyPressed)
|| !mLIMEPref.getPhysicalKeyboardType().equals("normal_keyboard")){
return false;
}
if(DEBUG) Log.i(TAG, "handleSelkey():primarycode:"+primaryCode);
int i = -1;
if (mComposing.length() > 0 && !mEnglishOnly) { //Jeremy '12,4,29 use mEnglishOnly instead of onIM
String selkey="";
// Jeremy '12,7,5 rewrite the selkey processing
if(disable_physical_selection && hasPhysicalKeyPressed){
selkey = "";
}else{
try{selkey=SearchSrv.getSelkey();
} catch (RemoteException e) {}
String mixedModeSelkey = "`";
if(hasSymbolMapping && !activeIM.equals("dayi")
&& ! (activeIM.equals("phonetic")
&& mLIMEPref.getPhoneticKeyboardType().equals("standard")) ){
mixedModeSelkey = " ";
}
int selkeyOption = mLIMEPref.getSelkeyOption();
if(selkeyOption ==1) selkey = mixedModeSelkey +selkey;
else if (selkeyOption ==2) selkey = mixedModeSelkey + " " +selkey;
i = selkey.indexOf((char) primaryCode);
//Jeremy '12,7,11 bypass space as first tone for phonetic
if( i>=0 && selkey.substring(i, i+1).equals(" ")
&& primaryCode == MY_KEYCODE_SPACE && activeIM.equals("phonetic")
//&& mLIMEPref.getParameterBoolean("doLDPhonetic", true)
&& !(mComposing.toString().endsWith(" ")|| mComposing.length()==0 ) ) {
return false;
}
}
} else if(mEnglishOnly
|| (selectedCandidate != null && selectedCandidate.isDictionary()&& !mEnglishOnly)) { //Jeremy '12,4,29 use mEnglishOnly instead of onIM
// related candidates view
i = relatedSelkey.indexOf(primaryCode);
}
if(i<0 || i >= mCandidateList.size()){
return false;
}
else{
pickCandidateManually(i);
return true;
}
}
/**
* This method construct candidate view and add key code to composing object
*
* @param primaryCode
* @param keyCodes
* @throws RemoteException
*/
private void handleCharacter(int primaryCode, int[] keyCodes) {
//Jeremy '11,6,9 Cleaned code!!
if(DEBUG)
Log.i(TAG,"handleCharacter():primaryCode:" + primaryCode
+ ", metaState = " + mMetaState
+ ", hasphysicalKeyPressed = " + hasPhysicalKeyPressed
+ ", currentSoftKeyboard=" + currentSoftKeyboard );
//Jeremy '11,6,6 processing physical keyboard selkeys.
//Move here '11,6,9 to have lower priority than hasnumbermapping
if (hasPhysicalKeyPressed && (mCandidateView != null && hasCandidatesShown)){ //Replace isCandidateShown() with hasCandidatesShown by Jeremy '12,5,6
if(handleSelkey(primaryCode, keyCodes)) {
updateShiftKeyState(getCurrentInputEditorInfo());
if(DEBUG)
Log.i(TAG, "handleCharacter() sel key found return now");
return;
}
}
if (!mEnglishOnly) {
InputConnection ic=getCurrentInputConnection();
if (DEBUG)
Log.i(TAG,"HandleCharacter():"
+ " ic != null:" + (ic!=null)
+ " isValidLetter:"+ isValidLetter(primaryCode)
+ " isValidDigit:" + isValidDigit(primaryCode)
+ " isValideSymbol:" + isValidSymbol(primaryCode)
+ " hasSymbolMapping:" + hasSymbolMapping
+ " hasNumberMapping:" + hasNumberMapping
+ " (primaryCode== MY_KEYCODE_SPACE && keyboardSelection.equals(phonetic):" + (primaryCode== MY_KEYCODE_SPACE && activeIM.equals("phonetic"))
+ " mEnglishOnly:" + mEnglishOnly);
if((!hasSymbolMapping)
&& (primaryCode==','||primaryCode=='.') && !mEnglishOnly ){ // Chinese , and . processing //Jeremy '12,4,29 use mEnglishOnly instead of onIM
mComposing.append((char) primaryCode);
//InputConnection ic=getCurrentInputConnection();
if(ic!=null) ic.setComposingText(mComposing, 1);
updateCandidates();
//misMatched = mComposing.toString();
}else if (!hasSymbolMapping && !hasNumberMapping //Jeremy '11,10.19 fixed to bypass number key in et26 and hsu
&&( isValidLetter(primaryCode)
|| (primaryCode== MY_KEYCODE_SPACE && activeIM.equals("phonetic")) ) //Jeremy '11,9,6 for et26 and hsu
&& !mEnglishOnly) { //Jeremy '12,4,29 use mEnglishOnly instead of onIM
//Log.i(TAG,"handlecharacter(), onIM and no number and no symbol mapping");
mComposing.append((char) primaryCode);
//InputConnection ic=getCurrentInputConnection();
if(ic!=null) ic.setComposingText(mComposing, 1);
updateCandidates();
//misMatched = mComposing.toString();
} else if (!hasSymbolMapping
&& hasNumberMapping
&& (isValidLetter(primaryCode) || isValidDigit(primaryCode))
&& !mEnglishOnly) { //Jeremy '12,4,29 use mEnglishOnly instead of onIM
mComposing.append((char) primaryCode);
//InputConnection ic=getCurrentInputConnection();
if(ic!=null) ic.setComposingText(mComposing, 1);
updateCandidates();
//misMatched = mComposing.toString();
} else if (hasSymbolMapping
&& !hasNumberMapping
&& ( isValidLetter(primaryCode) || isValidSymbol(primaryCode)
|| (primaryCode== MY_KEYCODE_SPACE && activeIM.equals("phonetic"))) //Jeremy '11,9,6 for chacha
&& !mEnglishOnly) { //Jeremy '12,4,29 use mEnglishOnly instead of onIM
mComposing.append((char) primaryCode);
//InputConnection ic=getCurrentInputConnection();
if(ic!=null) ic.setComposingText(mComposing, 1);
updateCandidates();
//misMatched = mComposing.toString();
} else if (hasSymbolMapping && !hasNumberMapping && activeIM.equals("array")
&& mComposing != null && mComposing.length() >= 1
&& getCurrentInputConnection().getTextBeforeCursor(1, 1).charAt(0) == 'w'
&& Character.isDigit((char)primaryCode)
&& !mEnglishOnly) { //Jeremy '12,4,29 use mEnglishOnly instead of onIM
// 27.May.2011 Art : This is the method to check user input type
// if first previous character is w and second char is number then enable im mode.
mComposing.append((char) primaryCode);
//InputConnection ic=getCurrentInputConnection();
if(ic!=null) ic.setComposingText(mComposing, 1);
updateCandidates();
//misMatched = mComposing.toString();
} else if (hasSymbolMapping
&& hasNumberMapping
&& (isValidSymbol(primaryCode)
|| (primaryCode== MY_KEYCODE_SPACE && activeIM.equals("phonetic"))
|| isValidLetter(primaryCode) || isValidDigit(primaryCode)) && !mEnglishOnly) { //Jeremy '12,4,29 use mEnglishOnly instead of onIM
mComposing.append((char) primaryCode);
//InputConnection ic=getCurrentInputConnection();
if(ic!=null) ic.setComposingText(mComposing, 1);
updateCandidates();
//misMatched = mComposing.toString();
} else {
pickHighlightedCandidate(); // check here.
if(ic!=null) ic.commitText(String.valueOf((char) primaryCode),1);
//Jeremy '12,4,21
finishComposing();
}
} else {
/*
* Handle when user input English Characters
*/
if(DEBUG)
Log.i(TAG, "handleCharacter() english only mode without prediction, committext = "
+ String.valueOf((char) primaryCode) );
if (isInputViewShown()) {
if (mInputView.isShifted()) {
primaryCode = Character.toUpperCase(primaryCode);
}
}
if (mLIMEPref.getEnglishPrediction() && mPredictionOn
&& ( !hasPhysicalKeyPressed || mLIMEPref.getEnglishPredictionOnPhysicalKeyboard())
) {
if (Character.isLetter((char) primaryCode)) {
this.tempEnglishWord.append((char) primaryCode);
this.updateEnglishPrediction();
}else{
resetTempEnglishWord();
this.updateEnglishPrediction();
}
}
getCurrentInputConnection().commitText(
String.valueOf((char) primaryCode), 1);
}
if(!(!hasPhysicalKeyPressed && hasDistinctMultitouch))
updateShiftKeyState(getCurrentInputEditorInfo());
}
private void handleClose() {
if(DEBUG) Log.i(TAG,"handleClose()");
// cancel candidate view if it's shown
//Jeremy '12,4,23 need to check here.
finishComposing();
requestHideSelf(0);
mInputView.closing();
}
private void checkToggleCapsLock() {
if (mInputView.getKeyboard().isShifted()) {
toggleCapsLock();
}
}
private void toggleCapsLock() {
mCapsLock = !mCapsLock;
if (mKeyboardSwitcher.isAlphabetMode()) {
((LIMEKeyboard) mInputView.getKeyboard()).setShiftLocked(mCapsLock);
} else {
if (mCapsLock) {
if (DEBUG) {
Log.i(TAG, "toggleCapsLock():mCapsLock:true");
}
if (!mKeyboardSwitcher.isShifted())
mKeyboardSwitcher.toggleShift();
((LIMEKeyboard) mInputView.getKeyboard()).setShiftLocked(true);
} else {
if (DEBUG) {
Log.i(TAG,"toggleCapsLock():mCapsLock:false");
}
((LIMEKeyboard) mInputView.getKeyboard()).setShiftLocked(false);
if (mKeyboardSwitcher.isShifted())
mKeyboardSwitcher.toggleShift();
}
}
}
public boolean isWordSeparator(int code) {
//Jeremy '11,5,31
String separators = getResources().getString(R.string.word_separators);
return separators.contains(String.valueOf((char)code));
}
//Jeremy '12,5,11 add return value from mCandidate.takeselectedsuggestion()
public boolean pickHighlightedCandidate() {
if(mCandidateView == null)
return false;
else
return mCandidateView.takeSelectedSuggestion();
}
public void requestFullRecords(boolean isRelated) {
if (DEBUG)
Log.i(TAG,"requestFullRecords()");
if(isRelated)
this.updateRelatedWord(true);
else
//updateCandidates to get full records.
this.updateCandidates(true);
}
public void pickCandidateManually(int index) {
if (DEBUG)
Log.i(TAG,"pickCandidateManually():"
+ "Pick up candidate at index : " + index);
// This is to prevent if user select the index more than the list
if(mCandidateList != null && index >= mCandidateList.size() ){
return;
}
if (mCandidateList != null && mCandidateList.size() > 0) {
selectedCandidate = mCandidateList.get(index);
selectedIndex = index;
}
InputConnection ic=getCurrentInputConnection();
if (mCompletionOn && mCompletions != null && index >= 0
&& selectedCandidate.isDictionary()
&& index < mCompletions.length ) {
CompletionInfo ci = mCompletions[index];
if(ic!=null) ic.commitCompletion(ci);
if (DEBUG)
Log.i(TAG, "pickSuggestionManually():mCompletionOn:" + mCompletionOn);
} else if ((mComposing.length() > 0
||selectedCandidate != null && selectedCandidate.isDictionary()) && !mEnglishOnly) {
//Jeremy '12,4,29 use mEnglishOnly instead of onIM
commitTyped(ic);
} else if (mLIMEPref.getEnglishPrediction() && tempEnglishList != null
&& tempEnglishList.size() > 0) {
if(ic!=null) ic.commitText(
this.tempEnglishList.get(index).getWord()
.substring(tempEnglishWord.length())
+ " ", 0);
resetTempEnglishWord();
//Mapping temp = new Mapping();
//temp.setWord("");
//temp.setDictionary(true);
//Jermy '11,8,14
clearSuggestions();
}
if(currentSoftKeyboard.indexOf("wb") != -1){
ic.setComposingText("", 0);
}
}
public void swipeRight() {
//if (mCompletionOn) {
pickHighlightedCandidate();
//}
}
public void swipeLeft() {
handleBackspace();
}
public void swipeDown() {
handleClose();
}
public void swipeUp() {
handleOptions();
}
/**
* First method to call after key press
*/
public void onPress(int primaryCode) {
if(DEBUG)
Log.i(TAG, "onPress(): code = " + primaryCode);
// Record key press time (press down)
//keyPressTime = System.currentTimeMillis();
// To identify the source of character (Software keyboard or physical
// keyboard)
hasPhysicalKeyPressed = false;
if (hasDistinctMultitouch && primaryCode == LIMEBaseKeyboard.KEYCODE_SHIFT) {
hasShiftPress = true;
hasShiftCombineKeyPressed = false;
handleShift();
}else if (hasDistinctMultitouch && hasShiftPress){
hasShiftCombineKeyPressed = true;
}
doVibrateSound(primaryCode);
try {
} catch (Exception e) {
e.printStackTrace();
}
}
public void doVibrateSound(int primaryCode) {
if(DEBUG) Log.i(TAG,"doVibrateSound()");
if (hasVibration) {
//Jeremy '11,9,1 add preference on vibrate level
mVibrator.vibrate(mLIMEPref.getVibrateLevel());
}
if (hasSound) {
int sound = AudioManager.FX_KEYPRESS_STANDARD;
switch (primaryCode) {
case LIMEBaseKeyboard.KEYCODE_DELETE:
sound = AudioManager.FX_KEYPRESS_DELETE;
break;
case MY_KEYCODE_ENTER:
sound = AudioManager.FX_KEYPRESS_RETURN;
break;
case MY_KEYCODE_SPACE:
sound = AudioManager.FX_KEYPRESS_SPACEBAR;
break;
}
mAudioManager.playSoundEffect(sound, FX_VOLUME);
}
}
/**
* Last method to execute when key release
*/
public void onRelease(int primaryCode) {
if(DEBUG)
Log.i(TAG, "onRelease(): code = " + primaryCode);
if(hasDistinctMultitouch && primaryCode == LIMEBaseKeyboard.KEYCODE_SHIFT ){
hasShiftPress = false;
if (hasShiftCombineKeyPressed) {
hasShiftCombineKeyPressed = false;
updateShiftKeyState(getCurrentInputEditorInfo());
}
}else if(hasDistinctMultitouch && !hasShiftPress){
updateShiftKeyState(getCurrentInputEditorInfo());
}
}
public boolean isValidTime(Date target) {
Calendar srcCal = Calendar.getInstance();
srcCal.setTime(new Date());
Calendar destCal = Calendar.getInstance();
destCal.setTime(target);
if (srcCal.getTimeInMillis() - destCal.getTimeInMillis() < 1800000) {
return true;
} else {
return false;
}
}
@Override
public void onDestroy() {
if(DEBUG)
Log.i(TAG,"onDestroy()");
//jeremy 12,4,21 need to check again---
//clearComposing(true); see no need to do this '12,4,21
super.onDestroy();
}
@Override
public void onUpdateCursor(Rect newCursor) {
if(DEBUG)
Log.i(TAG, "onUpdateCursor(): Top:"
+ newCursor.top + ". Right:" + newCursor.right
+ ". bottom:" + newCursor.bottom + ". left:" + newCursor.left );
if(mCandidateView!=null)
mCandidateView.onUpdateCursor(newCursor);
super.onUpdateCursor(newCursor);
}
@Override
public void onCancel() {
if(DEBUG)
Log.i(TAG,"onCancel()");
//clearComposing(); Jeremy '12,4,10 avoid clearcomposing when user slide outside the candidate area
}
//jeremy '11,9, 5 hideCanddiate when inputView is closed
@Override
public void updateInputViewShown() {
if(mInputView == null) return;
if(DEBUG) Log.i(TAG, "updateInputViewShown(): mInputView.isShown(): " + mInputView.isShown());
super.updateInputViewShown();
if(!mInputView.isShown() && !hasPhysicalKeyPressed)
hideCandidateView();
}
@Override
public void onFinishInputView(boolean finishingInput) {
if(DEBUG)
Log.i(TAG,"onFinishInputView()");
super.onFinishInputView(finishingInput);
hideCandidateView(); //Jeremy '12,5,7 hideCandiate when inputview is closed but not yet leave the original field (onfinishinput() will not called).
}
/**
* Experimental start voice input
*
*/
private void startVoiceInput(){
if(DEBUG)
Log.i(TAG, "startVoiceInput()");
String voiceid= LIMEUtilities.isVoiceSearchServiceExist(getBaseContext()) ;
if(voiceid != null)
this.switchInputMethod(voiceid);
}
}