package idv.Zero.KerKerInput.Methods; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.util.Log; import android.view.KeyEvent; import idv.Zero.KerKerInput.KerKerInputCore; import idv.Zero.KerKerInput.Keyboard; import idv.Zero.KerKerInput.R; import idv.Zero.KerKerInput.Methods.BPMFInputHelpers.ZhuYinComponentHelper; public class PINYINInput extends idv.Zero.KerKerInput.IKerKerInputMethod { private enum InputState {STATE_INPUT, STATE_CHOOSE, STATE_SUGGEST}; private InputState currentState; private String inputBufferRaw = ""; private List<CharSequence> _currentCandidates; private HashMap<Character, String> K2N; private HashMap<Character, Character> N2K; private String _dbpath; private String _wordCompDBPath; private String _phraseCompDBPath; private String _lastInput; private String _lastLastInput; private int _currentPage; private int _totalPages; private SQLiteDatabase db; private SQLiteDatabase wordCompDB; private SQLiteDatabase phraseCompDB; private char last; private char last2; private char last3; private boolean sound_one; private boolean key_blank; public void initInputMethod(KerKerInputCore core) { super.initInputMethod(core); initKeyNameData(); _currentPage = 0; _currentCandidates = new ArrayList<CharSequence>(); Context c = core.getFrontend(); _dbpath = c.getDatabasePath("cin.db").toString(); _wordCompDBPath = c.getDatabasePath("wc.db").toString(); _phraseCompDBPath = c.getDatabasePath("pc.db").toString(); _lastInput = ""; _lastLastInput = ""; try { db = SQLiteDatabase.openDatabase(_dbpath, null, SQLiteDatabase.OPEN_READONLY); db.setLocale(Locale.TRADITIONAL_CHINESE); db.close(); } catch(SQLiteException ex) { System.out.println("Error, no database file found. Copying..."); // Create the database (and the directories required) then close it. db = c.openOrCreateDatabase("cin.db", 0, null); db.close(); try { OutputStream dos = new FileOutputStream(_dbpath); InputStream dis = c.getResources().openRawResource(R.raw.bpmf); int size = dis.available(); byte[] buffer = new byte[size]; dis.read(buffer); dos.write(buffer); dos.flush(); dos.close(); dis.close(); } catch (IOException e) { e.printStackTrace(); Log.e("PINYINInput", "excepted1: " + e.getMessage()); } } try { wordCompDB = SQLiteDatabase.openDatabase(_wordCompDBPath, null, SQLiteDatabase.OPEN_READONLY); wordCompDB.setLocale(Locale.TRADITIONAL_CHINESE); wordCompDB.close(); } catch(SQLiteException ex) { System.out.println("Error, no database file found. Copying..."); // Create the database (and the directories required) then close it. wordCompDB = c.openOrCreateDatabase("wc.db", 0, null); wordCompDB.close(); try { OutputStream dos = new FileOutputStream(_wordCompDBPath); InputStream dis = c.getResources().openRawResource(R.raw.wordcomplete); int size = dis.available(); byte[] buffer = new byte[size]; dis.read(buffer); dos.write(buffer); dos.flush(); dos.close(); dis.close(); } catch (IOException e) { Log.e("PINYINInput", "excepted2: " + e.getMessage()); e.printStackTrace(); } } try { phraseCompDB = SQLiteDatabase.openDatabase(_phraseCompDBPath, null, SQLiteDatabase.OPEN_READONLY); phraseCompDB.setLocale(Locale.TRADITIONAL_CHINESE); phraseCompDB.close(); } catch(SQLiteException ex) { System.out.println("Error, no database file found. Copying..."); // Create the database (and the directories required) then close it. phraseCompDB = c.openOrCreateDatabase("pc.db", 0, null); phraseCompDB.close(); try { OutputStream dos = new FileOutputStream(_phraseCompDBPath); InputStream dis = c.getResources().openRawResource(R.raw.phrasecomplete); int size = dis.available(); byte[] buffer = new byte[size]; dis.read(buffer); dos.write(buffer); dos.flush(); dos.close(); dis.close(); } catch (IOException e) { Log.e("PINYINInput", "excepted3: " + e.getMessage()); e.printStackTrace(); } } try { phraseCompDB = SQLiteDatabase.openDatabase(_phraseCompDBPath, null, SQLiteDatabase.OPEN_READONLY); phraseCompDB.setLocale(Locale.TRADITIONAL_CHINESE); phraseCompDB.close(); } catch(SQLiteException ex) { System.out.println("Error, no database file found. Copying..."); // Create the database (and the directories required) then close it. phraseCompDB = c.openOrCreateDatabase("pc.db", 0, null); phraseCompDB.close(); try { OutputStream dos = new FileOutputStream(_phraseCompDBPath); InputStream dis = c.getResources().openRawResource(R.raw.phrasecomplete); int size = dis.available(); byte[] buffer = new byte[size]; dis.read(buffer); dos.write(buffer); dos.flush(); dos.close(); dis.close(); } catch (IOException e) { Log.e("PINYINInput", "excepted2: " + e.getMessage()); e.printStackTrace(); } } } public void onEnterInputMethod() { currentState = InputState.STATE_INPUT; inputBufferRaw = ""; _lastInput = ""; _lastLastInput = ""; updateCandidates(); // Copied, re-open it. db = SQLiteDatabase.openDatabase(_dbpath, null, SQLiteDatabase.OPEN_READONLY); db.setLocale(Locale.TRADITIONAL_CHINESE); wordCompDB = SQLiteDatabase.openDatabase(_wordCompDBPath, null, SQLiteDatabase.OPEN_READONLY); wordCompDB.setLocale(Locale.TRADITIONAL_CHINESE); phraseCompDB = SQLiteDatabase.openDatabase(_phraseCompDBPath, null, SQLiteDatabase.OPEN_READONLY); phraseCompDB.setLocale(Locale.TRADITIONAL_CHINESE); } public void onLeaveInputMethod() { db.close(); wordCompDB.close(); phraseCompDB.close(); } public String getName() { return "拼音"; } public Keyboard getDesiredKeyboard() { return new Keyboard(_core.getFrontend(), R.xml.kb_pinyin, R.id.mode_normal); } public void commitCurrentComposingBuffer() { commitText(getCompositeString()); } public boolean onKeyEvent(int keyCode, int[] keyCodes) { return handleBPMFKeyEvent(keyCode, keyCodes); } private boolean handleBPMFKeyEvent(int keyCode, int[] keyCodes) { if (currentState == InputState.STATE_INPUT || currentState == InputState.STATE_SUGGEST) { if (keyCode == Keyboard.KEYCODE_DELETE) { last3 = last = last2 = 0; if (inputBufferRaw.length() == 0) _core.getFrontend().sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); while (inputBufferRaw.length() > 0) { inputBufferRaw = inputBufferRaw.substring(0, inputBufferRaw.length() - 1); } //else // _core.getFrontend().sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); } /*else if (keyCode == 32) // space { if (_currentCandidates.size() > 0 && !(currentState == InputState.STATE_SUGGEST)) { currentState = InputState.STATE_CHOOSE; handleBPMFKeyEvent(32, null); } else { _core.getFrontend().sendKeyChar((char) keyCode); } }*/ else if (keyCode == 10) // RETURN { if (inputBufferRaw.length() > 0 && !(currentState == InputState.STATE_SUGGEST)) { commitText(getCompositeString()); } else _core.getFrontend().sendKeyChar((char) keyCode); } else { char c = (char)keyCode,cx=0; switch(c){ case 'n': if(last == 'a') cx='0'; else if(last == 'e' || last == 'i' || last == 'u'|| last == 'v') cx='p'; else if(last == 'o'){ cx='m'; last3 = last2; last2 = last; last = c; return true; } else cx='s'; break; case 'g': if(last2 == 'a' && last == 'n') cx=';'; else if((last3 == 'i' || last3 =='y') && (last2 == 'o' && last == 'n')){ inputBufferRaw = ZhuYinComponentHelper.getComposedRawString(inputBufferRaw, Character.toString('m')); cx='/'; } else if(last2 == 'o' && last == 'n'){ inputBufferRaw = ZhuYinComponentHelper.getComposedRawString(inputBufferRaw, Character.toString('j')); cx='/'; } else if(last == 'n'){ //inputBufferRaw = inputBufferRaw.substring(0, inputBufferRaw.length() - 1); cx='/'; } else cx='e'; break; case 'h': if(last == 'z') cx = '5'; else if(last == 'c') cx = 't'; else if(last == 's') cx = 'g'; else cx = 'c'; break; case 'r': if(last == 'e') cx='-'; else cx='b'; break; case 'i': if(last == 'y' || last == 'h' || last == 'r' || last == 'z' || last == 'c' || last == 'r'){ last3 = last2; last2 = last; last = c; return true; } else if(last == 'a') cx = '9'; else if(last == 'e') cx = 'o'; else if(last == 'u') cx = 'o'; else cx='u'; break; case 'u': if(last == 'o') cx = '.'; else if(last == 'i') cx = '.'; else if(last == 'y') cx = 'm'; else if(last == 'j' || last == 'q' || last == 'x' || last == 'u' || last == 'y') cx = 'm'; else cx='j'; break; case 'o': if(last == 'a') cx = 'l'; else if(last == 'i' && last2 !=0){ last3 = last2; last2 = last; last = c; return true; } else cx='i'; break; case 'e': if(last == 'y' ||last == 'i' ||last == 'u' || last == 'v') cx=','; else cx='k'; break; case ',': if(inputBufferRaw.length() == 0) _core.commitText(","); return true; case '.': if(inputBufferRaw.length() == 0) _core.commitText("。"); return true; case '?': if(inputBufferRaw.length() == 0) _core.commitText("?"); return true; case '!': if(inputBufferRaw.length() == 0) _core.commitText("!"); return true; case '6': if(inputBufferRaw.length() == 0) _core.commitText(":"); return true; case '7': if(inputBufferRaw.length() == 0) _core.commitText("「"); return true; case '8': if(inputBufferRaw.length() == 0) _core.commitText("」"); return true; case '9': if(inputBufferRaw.length() == 0) _core.commitText("、"); return true; case '0': if(inputBufferRaw.length() == 0) _core.commitText("…"); return true; case ' ': if(inputBufferRaw.length() == 0) _core.commitText(" "); return true; default : if (N2K.containsKey(c)) cx=N2K.get(c); } last3=last2; last2=last; last=c; inputBufferRaw = ZhuYinComponentHelper.getComposedRawString(inputBufferRaw, Character.toString(cx)); // 如果是音調符號,直接進入選字模式。 if (inputBufferRaw.length() > 0 && (c == ' ' || c == '1' || cx == '3' || cx == '4' || cx == '6' || cx == '7')){ if(c=='1') sound_one=true; else if(c==' ') key_blank=true; currentState = InputState.STATE_CHOOSE; } } _core.setCompositeBuffer(getCompositeString()); updateCandidates(); } if (currentState == InputState.STATE_CHOOSE) { switch (keyCode) { case Keyboard.KEYCODE_DELETE: currentState = InputState.STATE_INPUT; if (inputBufferRaw.length() > 0) { inputBufferRaw = inputBufferRaw.substring(0, inputBufferRaw.length() - 1); _core.setCompositeBuffer(getCompositeString()); updateCandidates(); } else Log.e("PINYINInput", "InputBuffer is requested to delete, but the buffer is empty"); break; // TODO: Make sure DPad & Keyboard L/R keyCode case -103: // DPad Left if (_currentPage > 0) _currentPage--; else _currentPage = _totalPages - 1; break; case -104: // DPad Right if (_currentPage < _totalPages - 1) _currentPage++; else _currentPage = 0; break; case ' ': case 10: keyCode = KeyEvent.KEYCODE_0; default: if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) { // This prevents user hit tone symbol twice makes program crash. // It's because first sign interpreted as bpmf symbol, and second one is treated as candidate choose. // Also prevent user select non-exist candidates on physical kb. if (_totalPages < 0) break; int CANDIDATES_PER_PAGE = (_currentCandidates.size() / _totalPages); if ((_currentPage * CANDIDATES_PER_PAGE + keyCode - KeyEvent.KEYCODE_0 - 1) < _currentCandidates.size()) { commitCandidate(_currentPage * CANDIDATES_PER_PAGE + keyCode - KeyEvent.KEYCODE_0 - 1); } } last3=last=last2=0; break; } } else if (currentState == InputState.STATE_SUGGEST) { switch (keyCode) { case -103: // DPad Left if (_currentPage > 0) _currentPage--; else _currentPage = _totalPages - 1; break; case -104: // DPad Right if (_currentPage < _totalPages - 1) _currentPage++; else _currentPage = 0; break; default: if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9 && (keyCode != 10 && keyCode != 32)) { // This prevents user hit tone symbol twice makes program crash. // It's because first sign interpreted as bpmf symbol, and second one is treated as candidate choose. // Also prevent user select non-exist candidates on physical kb. if (_totalPages < 0) break; int CANDIDATES_PER_PAGE = (_currentCandidates.size() / _totalPages); if ((_currentPage * CANDIDATES_PER_PAGE + keyCode - KeyEvent.KEYCODE_0 - 1) < _currentCandidates.size()) { commitCandidate(_currentPage * CANDIDATES_PER_PAGE + keyCode - KeyEvent.KEYCODE_0 - 1); } } break; } } return true; } private CharSequence getCompositeString() { StringBuilder str = new StringBuilder(); int length = inputBufferRaw.length(); for(int i=0;i<length;i++) { if (K2N.containsKey(inputBufferRaw.charAt(i))) str.append(K2N.get(inputBufferRaw.charAt(i))); // str.append(inputBufferRaw.charAt(i)); } return str.toString(); } private void updateCandidates() { if (inputBufferRaw.length() == 0) { _currentCandidates.clear(); if (_lastInput.equals("")) { _core.hideCandidatesView(); } else { try { Cursor currentQuery = wordCompDB.rawQuery("Select val from word_complete where key = '" + _lastInput + "' ORDER BY cnt DESC", null); int count = currentQuery.getCount(); int colIdx = currentQuery.getColumnIndex("val"); _currentCandidates = new ArrayList<CharSequence>(count); currentQuery.moveToNext(); for(int i=0;i<count;i++) { String ca = currentQuery.getString(colIdx); _currentCandidates.add(ca); currentQuery.moveToNext(); } currentQuery.close(); if(!(_lastInput.equals("") || _lastLastInput.equals(""))) { currentQuery = phraseCompDB.rawQuery("Select val from word_complete where key = '" + _lastLastInput + _lastInput + "' ORDER BY cnt DESC", null); count = currentQuery.getCount(); colIdx = currentQuery.getColumnIndex("val"); currentQuery.moveToNext(); for(int i=0;i<count;i++) { String ca = currentQuery.getString(colIdx); if (i==0) _currentCandidates.add(0, ca); else _currentCandidates.add(ca); currentQuery.moveToNext(); } currentQuery.close(); } _core.showCandidatesView(); _core.setCandidates(_currentCandidates); currentQuery.close(); } catch(Exception e) { Log.e("BPMFInput", "" + e.getMessage()); } finally {} } if ((!_lastInput.equals("")) && (!_lastLastInput.equals(""))) { try { Cursor currentQuery = phraseCompDB.rawQuery("Select val from word_complete where key = '" + _lastLastInput + _lastInput + "' ORDER BY cnt DESC", null); int count = currentQuery.getCount(); int colIdx = currentQuery.getColumnIndex("val"); currentQuery.moveToNext(); for(int i=0;i<count;i++) { String ca = currentQuery.getString(colIdx); _currentCandidates.add(ca); currentQuery.moveToNext(); } _core.showCandidatesView(); _core.setCandidates(_currentCandidates); currentQuery.close(); } catch(Exception e) { Log.e("BPMFInput", "" + e.getMessage()); } finally {} } return; } try { String query; if(sound_one){ query = "Select val from bpmf where key = '" + inputBufferRaw.toString() + "'"; sound_one = false; } else if(key_blank){ query = "Select val from bpmf where key = '" + inputBufferRaw.toString() + "' OR key = '" + inputBufferRaw.toString() + "7'"; key_blank = false; } else{ query = "Select val from bpmf where key >= '" + inputBufferRaw.toString() + "' AND key < '" + inputBufferRaw.toString() + "zzz'"; } Cursor currentQuery = db.rawQuery(query, null); if (currentQuery.getCount() == 0) { inputBufferRaw = inputBufferRaw.substring(0, inputBufferRaw.length() - 1); _currentCandidates.clear(); _core.setCompositeBuffer(getCompositeString()); //_core.showPopup(R.string.no_such_mapping); _core.hideCandidatesView(); currentState = InputState.STATE_INPUT; updateCandidates(); } else { int count = Math.min(currentQuery.getCount(), 50); int colIdx = currentQuery.getColumnIndex("val"); _currentCandidates = new ArrayList<CharSequence>(count); currentQuery.moveToNext(); String _ca = ""; for(int i=0;i<count;i++) { String ca = currentQuery.getString(colIdx); // bz: list are sorted by sqlite3, skip repeating word(s) if ( !ca.equals(_ca) ) { _currentCandidates.add(ca); } currentQuery.moveToNext(); _ca = ca; } _core.showCandidatesView(); _core.setCandidates(_currentCandidates); } currentQuery.close(); } catch(Exception e) {} finally { } } public void commitCandidate(int selectedCandidate) { if (selectedCandidate < 0) selectedCandidate = 0; if (_currentCandidates.size() == 0) return; commitText(_currentCandidates.get(selectedCandidate)); } public void setTotalPages(int totalPages) { _totalPages = totalPages; } public void setCurrentPage(int currentPage) { _currentPage = currentPage; } private void commitText(CharSequence str) { _core.commitText(str); String _history = (_lastInput + str.toString()) ; int _length = _history.length(); if (_length >=2) { _lastInput = _history.substring(_length-1); _lastLastInput = _history.substring(_length-2, _length-1); } else if (_length == 1) { _lastInput = _history.substring(_length-1); _lastLastInput = ""; } else { _lastInput = ""; _lastLastInput = ""; } inputBufferRaw = ""; updateCandidates(); currentState = InputState.STATE_SUGGEST; } private void initKeyNameData() { K2N = new HashMap<Character, String>(); K2N.put(',', "ㄝ"); K2N.put('-', "ㄦ"); K2N.put('.', "ㄡ"); K2N.put('/', "ㄥ"); K2N.put('0', "ㄢ"); K2N.put('1', "ㄅ"); K2N.put('2', "ㄉ"); K2N.put('3', "ˇ"); K2N.put('4', "ˋ"); K2N.put('5', "ㄓ"); K2N.put('6', "ˊ"); K2N.put('7', "˙"); K2N.put('8', "ㄚ"); K2N.put('9', "ㄞ"); K2N.put(';', "ㄤ"); K2N.put('a', "ㄇ"); K2N.put('b', "ㄖ"); K2N.put('c', "ㄏ"); K2N.put('d', "ㄎ"); K2N.put('e', "ㄍ"); K2N.put('f', "ㄑ"); K2N.put('g', "ㄕ"); K2N.put('h', "ㄘ"); K2N.put('i', "ㄛ"); K2N.put('j', "ㄨ"); K2N.put('k', "ㄜ"); K2N.put('l', "ㄠ"); K2N.put('m', "ㄩ"); K2N.put('n', "ㄙ"); K2N.put('o', "ㄟ"); K2N.put('p', "ㄣ"); K2N.put('q', "ㄆ"); K2N.put('r', "ㄐ"); K2N.put('s', "ㄋ"); K2N.put('t', "ㄔ"); K2N.put('u', "ㄧ"); K2N.put('v', "ㄒ"); K2N.put('w', "ㄊ"); K2N.put('x', "ㄌ"); K2N.put('y', "ㄗ"); K2N.put('z', "ㄈ"); N2K = new HashMap<Character, Character>(); N2K.put('b', '1'); N2K.put('p', 'q'); N2K.put('m', 'a'); N2K.put('f', 'z'); N2K.put('d', '2'); N2K.put('t', 'w'); N2K.put('n', 's'); N2K.put('l', 'x'); N2K.put('g', 'e'); N2K.put('k', 'd'); N2K.put('h', 'c'); N2K.put('j', 'r'); N2K.put('q', 'f'); N2K.put('x', 'v'); N2K.put('e', ','); N2K.put('r', 'b'); N2K.put('z', 'y'); N2K.put('c', 'h'); N2K.put('s', 'n'); N2K.put('i', 'u'); N2K.put('u', 'j'); N2K.put('v', 'm'); N2K.put('a', '8'); N2K.put('o', 'i'); N2K.put('e', 'k'); N2K.put('y', 'u'); N2K.put('w', 'j'); N2K.put('3', '3'); N2K.put('4', '4'); N2K.put('2', '6'); N2K.put('5', '7'); } }