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 BPMFInput 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 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;
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("BPMFInput", "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("BPMFInput", "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("BPMFInput", "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("BPMFInput", "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_zhuyin, 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)
{
_lastInput = _lastLastInput;
_lastLastInput = "";
if (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;
inputBufferRaw = ZhuYinComponentHelper.getComposedRawString(inputBufferRaw, Character.toString(c));
// 如果是音調符號,直接進入選字模式。
if (inputBufferRaw.length() > 0 && (c == '3' || c == '4' || c == '6' || c == '7'))
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("BPMFInput", "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);
}
}
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)));
}
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
{
// Cursor currentQuery = db.rawQuery("Select val from bpmf where key glob '" + inputBufferRaw.toString() + "*'", null);
Cursor currentQuery = db.rawQuery("Select val from bpmf where key >= '" + inputBufferRaw.toString() + "' AND key < '" + inputBufferRaw.toString() + "zzz' ORDER BY cnt DESC", 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', "ㄈ");
}
}