X-Git-Url: http://gitweb.fperrin.net/?a=blobdiff_plain;f=src%2Fcom%2Fhughes%2Fandroid%2Fdictionary%2FDictionaryActivity.java;h=922b9728cda321c11c62614ba554414496ce8025;hb=b8734478a4b5c46cdb260fadad9753cdc85809ae;hp=bc6ad406b74b5f57b192c407db800419bf0be33f;hpb=4c723c97e27e429b006a0dc8c3dfaf304d52e95c;p=Dictionary.git diff --git a/src/com/hughes/android/dictionary/DictionaryActivity.java b/src/com/hughes/android/dictionary/DictionaryActivity.java old mode 100755 new mode 100644 index bc6ad40..922b972 --- a/src/com/hughes/android/dictionary/DictionaryActivity.java +++ b/src/com/hughes/android/dictionary/DictionaryActivity.java @@ -1,536 +1,724 @@ -package com.hughes.android.dictionary; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.RandomAccessFile; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicBoolean; - -import android.app.AlertDialog; -import android.app.ListActivity; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.graphics.Typeface; -import android.os.Bundle; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.text.Editable; -import android.text.Spannable; -import android.text.TextWatcher; -import android.text.style.StyleSpan; -import android.util.Log; -import android.view.ContextMenu; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.MenuItem.OnMenuItemClickListener; -import android.view.View.OnClickListener; -import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ListView; -import android.widget.TableLayout; -import android.widget.TableRow; -import android.widget.TextView; -import android.widget.AdapterView.OnItemLongClickListener; -import android.widget.AdapterView.OnItemSelectedListener; - -import com.hughes.android.dictionary.Dictionary.IndexEntry; -import com.hughes.android.dictionary.Dictionary.LanguageData; -import com.hughes.android.dictionary.Dictionary.Row; - -public class DictionaryActivity extends ListActivity { - - String WORD_LIST_FILE; - String DICT_FILE; - String DICT_FETCH_URL; - - static final Intent preferencesIntent = new Intent().setClassName(PreferenceActivity.class.getPackage().getName(), PreferenceActivity.class.getCanonicalName()); - - private final Handler uiHandler = new Handler(); - private final Executor searchExecutor = Executors.newSingleThreadExecutor(); - private final DictionaryListAdapter dictionaryListAdapter = new DictionaryListAdapter(); - - // Never null. - private File wordList = new File("/sdcard/wordList.txt"); - - // Can be null. - private File dictFile = null; - private RandomAccessFile dictRaf = null; - private Dictionary dictionary = null; - private LanguageData activeLangaugeData = null; - - private SearchOperation searchOperation = null; - private int selectedRowIndex = -1; - private int selectedTokenRowIndex = -1; - - - /** Called when the activity is first created. */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - WORD_LIST_FILE = getResources().getString(R.string.wordListFileKey); - DICT_FILE = getResources().getString(R.string.dictFileKey); - DICT_FETCH_URL = getResources().getString(R.string.dictFetchUrlKey); - - Log.d("THAD", "onCreate"); - } - - @Override - public void onResume() { - super.onResume(); - - // Have to close, because we might have downloaded a new copy of the dictionary. - closeCurrentDictionary(); - - final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this); - wordList = new File(settings.getString(WORD_LIST_FILE, wordList.getAbsolutePath())); - final File newDictFile = new File(settings.getString(DICT_FILE, "/sdcard/de-en.dict")); - dictFile = newDictFile; - Log.d("THAD", "wordList=" + wordList); - Log.d("THAD", "dictFile=" + dictFile); - - if (!dictFile.canRead()) { - dictionaryListAdapter.notifyDataSetChanged(); - Log.d("THAD", "Unable to read dictionary file."); - final AlertDialog alert = new AlertDialog.Builder(DictionaryActivity.this).create(); - alert.setMessage("Unable to read dictionary file: " + dictFile.getAbsolutePath()); - alert.setButton("Download dictionary", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - startDownloadDictActivity(); - }}); - alert.show(); - return; - } - - try { - dictRaf = new RandomAccessFile(dictFile, "r"); - dictionary = new Dictionary(dictRaf); - activeLangaugeData = dictionary.languageDatas[Entry.LANG1]; - dictionaryListAdapter.notifyDataSetChanged(); - } catch (Exception e) { - throw new RuntimeException(e); - } - - setContentView(R.layout.main); - - getSearchText().addTextChangedListener(new DictionaryTextWatcher()); - - setListAdapter(dictionaryListAdapter); - - // Language button. - final Button langButton = (Button) findViewById(R.id.LangButton); - langButton.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - switchLanguage(); - }}); - updateLangButton(); - - final Button upButton = (Button) findViewById(R.id.UpButton); - upButton.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - if (dictionary == null) { - return; - } - final int destRowIndex; - final Row tokenRow = activeLangaugeData.rows.get(selectedTokenRowIndex); - assert tokenRow.isToken(); - final int prevTokenIndex = tokenRow.getIndex() - 1; - if (selectedRowIndex == selectedTokenRowIndex && selectedRowIndex > 0) { - destRowIndex = activeLangaugeData.sortedIndex.get(prevTokenIndex).startRow; - } else { - destRowIndex = selectedTokenRowIndex; - } - jumpToRow(destRowIndex); - }}); - final Button downButton = (Button) findViewById(R.id.DownButton); - downButton.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - if (dictionary == null) { - return; - } - final Row tokenRow = activeLangaugeData.rows.get(selectedTokenRowIndex); - assert tokenRow.isToken(); - final int nextTokenIndex = tokenRow.getIndex() + 1; - final int destRowIndex; - if (nextTokenIndex < activeLangaugeData.sortedIndex.size()) { - destRowIndex = activeLangaugeData.sortedIndex.get(nextTokenIndex).startRow; - } else { - destRowIndex = activeLangaugeData.rows.size() - 1; - } - jumpToRow(destRowIndex); - }}); - - // ContextMenu. - registerForContextMenu(getListView()); - - // ItemSelectedListener. - getListView().setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView arg0, View arg1, int rowIndex, - long arg3) { - Log.d("THAD", "onItemSelected: " + rowIndex); - selectedRowIndex = rowIndex; - selectedTokenRowIndex = activeLangaugeData.getIndexEntryForRow(rowIndex).startRow; - updateSearchText(); - } - - public void onNothingSelected(AdapterView arg0) { - }}); - - - // LongClickListener. - getListView().setOnItemLongClickListener((new OnItemLongClickListener() { - public boolean onItemLongClick(AdapterView arg0, View arg1, int rowIndex, - long arg3) { - selectedRowIndex = rowIndex; - return false; - } - })); - - onSearchTextChange(""); - } - - - @Override - public void onStop() { - super.onStop(); - closeCurrentDictionary(); - } - - private void closeCurrentDictionary() { - dictionary = null; - activeLangaugeData = null; - try { - if (dictRaf != null) { - dictRaf.close(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - dictRaf = null; - } - - public String getSelectedRowRawText() { - return activeLangaugeData.rowToString(activeLangaugeData.rows.get(selectedRowIndex)); - } - - public EditText getSearchText() { - return (EditText) findViewById(R.id.SearchText); - } - - // ---------------------------------------------------------------- - // OptionsMenu - // ---------------------------------------------------------------- - - private MenuItem switchLanguageMenuItem = null; - - - @Override - public boolean onCreateOptionsMenu(final Menu menu) { - switchLanguageMenuItem = menu.add("Switch to language."); - switchLanguageMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener(){ - public boolean onMenuItemClick(final MenuItem menuItem) { - switchLanguage(); - return false; - }}); - - final MenuItem preferences = menu.add("Preferences..."); - preferences.setOnMenuItemClickListener(new OnMenuItemClickListener(){ - public boolean onMenuItemClick(final MenuItem menuItem) { - startActivity(preferencesIntent); - return false; - }}); - - final MenuItem about = menu.add("About..."); - about.setOnMenuItemClickListener(new OnMenuItemClickListener(){ - public boolean onMenuItemClick(final MenuItem menuItem) { - final Intent intent = new Intent().setClassName(AboutActivity.class.getPackage().getName(), AboutActivity.class.getCanonicalName()); - final StringBuilder currentDictInfo = new StringBuilder(); - if (dictionary == null) { - currentDictInfo.append("No dictionary loaded."); - } else { - currentDictInfo.append(dictionary.dictionaryInfo).append("\n\n"); - currentDictInfo.append("Entry count: " + dictionary.entries.size()).append("\n"); - for (int i = 0; i < 2; ++i) { - final LanguageData languageData = dictionary.languageDatas[i]; - currentDictInfo.append(languageData.language.symbol).append(":\n"); - currentDictInfo.append(" Unique token count: " + languageData.sortedIndex.size()).append("\n"); - currentDictInfo.append(" Row count: " + languageData.rows.size()).append("\n"); - } - } - intent.putExtra(AboutActivity.CURRENT_DICT_INFO, currentDictInfo.toString()); - startActivity(intent); - return false; - }}); - - final MenuItem download = menu.add("Download dictionary..."); - download.setOnMenuItemClickListener(new OnMenuItemClickListener(){ - public boolean onMenuItemClick(final MenuItem menuItem) { - startDownloadDictActivity(); - return false; - }}); - - return true; - } - - @Override - public boolean onPrepareOptionsMenu(final Menu menu) { - if (dictionary != null) { - switchLanguageMenuItem.setTitle(String.format("Switch to %s", dictionary.languageDatas[Entry.otherLang(activeLangaugeData.lang)].language.symbol)); - } - switchLanguageMenuItem.setEnabled(dictionary != null); - return super.onPrepareOptionsMenu(menu); - } - - void switchLanguage() { - if (dictionary == null) { - return; - } - activeLangaugeData = dictionary.languageDatas[(activeLangaugeData == dictionary.languageDatas[0]) ? 1 : 0]; - selectedRowIndex = 0; - selectedTokenRowIndex = 0; - updateLangButton(); - dictionaryListAdapter.notifyDataSetChanged(); - onSearchTextChange(getSearchText().getText().toString()); - } - - void updateLangButton() { - final Button langButton = (Button) findViewById(R.id.LangButton); - langButton.setText(activeLangaugeData.language.symbol); - } - - // ---------------------------------------------------------------- - // ContextMenu - // ---------------------------------------------------------------- - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - if (selectedRowIndex == -1) { - return; - } - final MenuItem addToWordlist = menu.add("Add to wordlist: " + wordList.getName()); - addToWordlist.setOnMenuItemClickListener(new OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - final StringBuilder rawText = new StringBuilder(); - final String word = activeLangaugeData.getIndexEntryForRow(selectedRowIndex).word; - rawText.append(new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").format(new Date())).append("\t"); - rawText.append(word).append("\t"); - rawText.append(getSelectedRowRawText()); - Log.d("THAD", "Writing : " + rawText); - try { - wordList.getParentFile().mkdirs(); - final PrintWriter out = new PrintWriter(new FileWriter(wordList, true)); - out.println(rawText.toString()); - out.close(); - } catch (IOException e) { - Log.e("THAD", "Unable to append to " + wordList.getAbsolutePath(), e); - final AlertDialog alert = new AlertDialog.Builder(DictionaryActivity.this).create(); - alert.setMessage("Failed to append to file: " + wordList.getAbsolutePath()); - alert.show(); - } - return false; - } - }); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (event.getUnicodeChar() != 0) { - final EditText searchText = getSearchText(); - if (!searchText.hasFocus()) { - searchText.setText("" + (char)event.getUnicodeChar()); - onSearchTextChange(searchText.getText().toString()); - searchText.requestFocus(); - } - return true; - } - return super.onKeyDown(keyCode, event); - } - - @Override - protected void onListItemClick(ListView l, View v, int row, long id) { - selectedRowIndex = row; - Log.d("THAD", "Clicked: " + getSelectedRowRawText()); - openContextMenu(getListView()); - } - - void onSearchTextChange(final String searchText) { - Log.d("THAD", "onSearchTextChange: " + searchText); - if (dictionary == null) { - return; - } - if (searchOperation != null) { - searchOperation.interrupted.set(true); - } - searchOperation = new SearchOperation(searchText); - searchExecutor.execute(searchOperation); - } - - private void jumpToRow(final int rowIndex) { - Log.d("THAD", "jumpToRow: " + rowIndex); - selectedRowIndex = rowIndex; - selectedTokenRowIndex = activeLangaugeData.getIndexEntryForRow(rowIndex).startRow; - getListView().setSelection(rowIndex); - getListView().setSelected(true); // TODO: is this doing anything? - updateSearchText(); - } - - private void updateSearchText() { - final EditText searchText = getSearchText(); - if (!searchText.hasFocus()) { - final String word = activeLangaugeData.getIndexEntryForRow(selectedRowIndex).word; - if (!word.equals(searchText.getText().toString())) { - Log.d("THAD", "updateSearchText: setText: " + word); - searchText.setText(word); - } - } - } - - private void startDownloadDictActivity() { - final Intent intent = new Intent().setClassName( - DownloadActivity.class.getPackage().getName(), - DownloadActivity.class.getCanonicalName()); - final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(DictionaryActivity.this); - final String dictFetchUrl = settings.getString(DICT_FETCH_URL, getResources().getString(R.string.dictFetchUrl)); - final String dictFileName = settings.getString(DICT_FILE, getResources().getString(R.string.dictFile)); - intent.putExtra(DownloadActivity.SOURCE, dictFetchUrl); - intent.putExtra(DownloadActivity.DEST, dictFileName); - startActivity(intent); - } - - private final class SearchOperation implements Runnable { - final String searchText; - final AtomicBoolean interrupted = new AtomicBoolean(false); - - public SearchOperation(final String searchText) { - this.searchText = searchText; - } - - public void run() { - Log.d("THAD", "SearchOperation: " + searchText); - final int indexLocation = activeLangaugeData.lookup(searchText, interrupted); - if (interrupted.get()) { - return; - } - final IndexEntry indexEntry = activeLangaugeData.sortedIndex - .get(indexLocation); - uiHandler.post(new Runnable() { - public void run() { - jumpToRow(indexEntry.startRow); - } - }); - } - } - - private class DictionaryListAdapter extends BaseAdapter { - - public int getCount() { - return dictionary != null ? activeLangaugeData.rows.size() : 0; - } - - public Dictionary.Row getItem(int rowIndex) { - assert rowIndex < activeLangaugeData.rows.size(); - return activeLangaugeData.rows.get(rowIndex); - } - - public long getItemId(int rowIndex) { - return rowIndex; - } - - public View getView(final int rowIndex, final View convertView, - final ViewGroup parent) { - final Row row = getItem(rowIndex); - - // Token row. - if (row.isToken()) { - TextView result = null; - if (convertView instanceof TextView) { - result = (TextView) convertView; - } else { - result = new TextView(parent.getContext()); - } - result.setText(activeLangaugeData.rowToString(row)); - result.setTextAppearance(parent.getContext(), - android.R.style.TextAppearance_Large); - result.setClickable(false); - return result; - } - - // Entry row(s). - final TableLayout result = new TableLayout(parent.getContext()); - - final Entry entry = dictionary.entries.get(row.getIndex()); - final int rowCount = entry.getRowCount(); - for (int r = 0; r < rowCount; ++r) { - final TableRow tableRow = new TableRow(result.getContext()); - - TextView column1 = new TextView(tableRow.getContext()); - TextView column2 = new TextView(tableRow.getContext()); - final TableRow.LayoutParams layoutParams = new TableRow.LayoutParams(); - layoutParams.weight = 0.5f; - - if (r>0){ - final TextView spacer = new TextView(tableRow.getContext()); - spacer.setText(r == 0 ? "• " : " • "); - tableRow.addView(spacer); - } - tableRow.addView(column1, layoutParams); - if (r > 0) { - final TextView spacer = new TextView(tableRow.getContext()); - spacer.setText(r == 0 ? "• " : " • "); - tableRow.addView(spacer); - } - tableRow.addView(column2, layoutParams); - - column1.setWidth(1); - column2.setWidth(1); - // column1.setTextAppearance(parent.getContext(), android.R.style.Text); - - // TODO: color words by gender - final String col1Text = entry.getAllText(activeLangaugeData.lang)[r]; - column1.setText(col1Text, TextView.BufferType.SPANNABLE); - final Spannable col1Spannable = (Spannable) column1.getText(); - int startPos = 0; - final String token = activeLangaugeData.getIndexEntryForRow(rowIndex).word; - while ((startPos = col1Text.indexOf(token, startPos)) != -1) { - col1Spannable.setSpan(new StyleSpan(Typeface.BOLD), startPos, startPos + token.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - startPos += token.length(); - } - - column2.setText(entry.getAllText(Entry.otherLang(activeLangaugeData.lang))[r], TextView.BufferType.NORMAL); - - result.addView(tableRow); - } - - return result; - } - } // DictionaryListAdapter - - private class DictionaryTextWatcher implements TextWatcher { - public void afterTextChanged(final Editable searchText) { - if (getSearchText().hasFocus()) { - // If they were typing to cause the change, update the UI. - onSearchTextChange(searchText.toString()); - } - } - - public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, - int arg3) { - } - - public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { - } - } - -} \ No newline at end of file +package com.hughes.android.dictionary; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.RandomAccessFile; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicBoolean; + +import android.app.ListActivity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Typeface; +import android.os.Bundle; +import android.os.Handler; +import android.preference.PreferenceManager; +import android.text.ClipboardManager; +import android.text.Editable; +import android.text.Spannable; +import android.text.TextWatcher; +import android.text.style.StyleSpan; +import android.util.Log; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.BaseAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.TableLayout; +import android.widget.TableRow; +import android.widget.TextView; +import android.widget.Toast; + +import com.hughes.android.dictionary.engine.Dictionary; +import com.hughes.android.dictionary.engine.Index; +import com.hughes.android.dictionary.engine.PairEntry; +import com.hughes.android.dictionary.engine.PairEntry.Pair; +import com.hughes.android.dictionary.engine.RowBase; +import com.hughes.android.dictionary.engine.TokenRow; +import com.hughes.android.dictionary.engine.TransliteratorManager; +import com.hughes.android.util.PersistentObjectCache; + +public class DictionaryActivity extends ListActivity { + + static final String LOG = "QuickDic"; + + static final int VIBRATE_MILLIS = 100; + + int dictIndex = 0; + RandomAccessFile dictRaf = null; + Dictionary dictionary = null; + int indexIndex = 0; + Index index = null; + + // package for test. + final Handler uiHandler = new Handler(); + private final Executor searchExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + return new Thread(r, "searchExecutor"); + } + }); + private SearchOperation currentSearchOperation = null; + + EditText searchText; + Button langButton; + + // Never null. + private File wordList = null; + private boolean saveOnlyFirstSubentry = false; + + // Visible for testing. + ListAdapter indexAdapter = null; + + final SearchTextWatcher searchTextWatcher = new SearchTextWatcher(); + + //private Vibrator vibrator = null; + + public DictionaryActivity() { + } + + public static Intent getIntent(final Context context, final int dictIndex, final int indexIndex, final String searchToken) { + setDictionaryPrefs(context, dictIndex, indexIndex, searchToken); + + final Intent intent = new Intent(); + intent.setClassName(DictionaryActivity.class.getPackage().getName(), DictionaryActivity.class.getName()); + return intent; + } + + public static void setDictionaryPrefs(final Context context, + final int dictIndex, final int indexIndex, final String searchToken) { + final SharedPreferences.Editor prefs = PreferenceManager.getDefaultSharedPreferences(context).edit(); + prefs.putInt(C.DICT_INDEX, dictIndex); + prefs.putInt(C.INDEX_INDEX, indexIndex); + prefs.putString(C.SEARCH_TOKEN, searchToken); + prefs.commit(); + } + + public static void clearDictionaryPrefs(final Context context) { + final SharedPreferences.Editor prefs = PreferenceManager.getDefaultSharedPreferences(context).edit(); + prefs.remove(C.DICT_INDEX); + prefs.remove(C.INDEX_INDEX); + prefs.remove(C.SEARCH_TOKEN); + prefs.commit(); + Log.d(LOG, "Removed default dictionary prefs."); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + ((DictionaryApplication)getApplication()).applyTheme(this); + + super.onCreate(savedInstanceState); + Log.d(LOG, "onCreate:" + this); + + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + + try { + PersistentObjectCache.init(this); + QuickDicConfig quickDicConfig = PersistentObjectCache.init( + this).read(C.DICTIONARY_CONFIGS, QuickDicConfig.class); + dictIndex = prefs.getInt(C.DICT_INDEX, 0) ; + final DictionaryConfig dictionaryConfig = quickDicConfig.dictionaryConfigs.get(dictIndex); + dictRaf = new RandomAccessFile(dictionaryConfig.localFile, "r"); + dictionary = new Dictionary(dictRaf); + } catch (Exception e) { + Log.e(LOG, "Unable to load dictionary.", e); + if (dictRaf != null) { + try { + dictRaf.close(); + } catch (IOException e1) { + Log.e(LOG, "Unable to close dictRaf.", e1); + } + dictRaf = null; + } + Toast.makeText(this, getString(R.string.invalidDictionary, "", e.getMessage()), Toast.LENGTH_LONG); + startActivity(DictionaryEditActivity.getIntent(dictIndex)); + finish(); + return; + } + + indexIndex = prefs.getInt(C.INDEX_INDEX, 0) % dictionary.indices.size(); + Log.d(LOG, "Loading index."); + index = dictionary.indices.get(indexIndex); + setListAdapter(new IndexAdapter(index)); + + // Pre-load the collators. + searchExecutor.execute(new Runnable() { + public void run() { + final long startMillis = System.currentTimeMillis(); + + TransliteratorManager.init(new TransliteratorManager.Callback() { + @Override + public void onTransliteratorReady() { + uiHandler.post(new Runnable() { + @Override + public void run() { + onSearchTextChange(searchText.getText().toString()); + } + }); + } + }); + + for (final Index index : dictionary.indices) { + Log.d(LOG, "Starting collator load for lang=" + index.sortLanguage.getSymbol()); + + final com.ibm.icu.text.Collator c = index.sortLanguage.getCollator(); + if (c.compare("pre-print", "preppy") >= 0) { + Log.e(LOG, c.getClass() + + " is buggy, lookups may not work properly."); + } + } + Log.d(LOG, "Loading collators took:" + + (System.currentTimeMillis() - startMillis)); + } + }); + + + setContentView(R.layout.dictionary_activity); + searchText = (EditText) findViewById(R.id.SearchText); + langButton = (Button) findViewById(R.id.LangButton); + + searchText.requestFocus(); + searchText.addTextChangedListener(searchTextWatcher); + final String search = prefs.getString(C.SEARCH_TOKEN, ""); + searchText.setText(search); + searchText.setSelection(0, search.length()); + Log.d(LOG, "Trying to restore searchText=" + search); + + final Button clearSearchTextButton = (Button) findViewById(R.id.ClearSearchTextButton); + clearSearchTextButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + onClearSearchTextButton(clearSearchTextButton); + } + }); + clearSearchTextButton.setVisibility(PreferenceManager.getDefaultSharedPreferences(this).getBoolean( + getString(R.string.showClearSearchTextButtonKey), true) ? View.VISIBLE + : View.GONE); + + final Button langButton = (Button) findViewById(R.id.LangButton); + langButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + onLanguageButton(); + } + }); + updateLangButton(); + + final Button upButton = (Button) findViewById(R.id.UpButton); + upButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + onUpDownButton(true); + } + }); + final Button downButton = (Button) findViewById(R.id.DownButton); + downButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + onUpDownButton(false); + } + }); + + getListView().setOnItemSelectedListener(new ListView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View arg1, final int position, + long id) { + if (!searchText.isFocused()) { + // TODO: don't do this if multi words are entered. + final RowBase row = (RowBase) getListAdapter().getItem(position); + Log.d(LOG, "onItemSelected: " + row.index()); + final TokenRow tokenRow = row.getTokenRow(true); + searchText.setText(tokenRow.getToken()); + } + } + + @Override + public void onNothingSelected(AdapterView arg0) { + } + }); + + // ContextMenu. + registerForContextMenu(getListView()); + + // Prefs. + wordList = new File(prefs.getString(getString(R.string.wordListFileKey), + getString(R.string.wordListFileDefault))); + saveOnlyFirstSubentry = prefs.getBoolean(getString(R.string.saveOnlyFirstSubentryKey), false); + //if (prefs.getBoolean(getString(R.string.vibrateOnFailedSearchKey), true)) { + // vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); + //} + Log.d(LOG, "wordList=" + wordList + ", saveOnlyFirstSubentry=" + saveOnlyFirstSubentry); + } + + @Override + protected void onResume() { + super.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (dictRaf == null) { + return; + } + setDictionaryPrefs(this, dictIndex, indexIndex, searchText.getText().toString()); + + // Before we close the RAF, we have to wind the current search down. + if (currentSearchOperation != null) { + Log.d(LOG, "Interrupting search to shut down."); + final SearchOperation searchOperation = currentSearchOperation; + currentSearchOperation = null; + searchOperation.interrupted.set(true); + synchronized (searchOperation) { + while (!searchOperation.done) { + try { + searchOperation.wait(); + } catch (InterruptedException e) { + Log.d(LOG, "Interrupted.", e); + } + } + } + } + + try { + Log.d(LOG, "Closing RAF."); + dictRaf.close(); + } catch (IOException e) { + Log.e(LOG, "Failed to close dictionary", e); + } + dictRaf = null; + } + + // -------------------------------------------------------------------------- + // Buttons + // -------------------------------------------------------------------------- + + private void onClearSearchTextButton(final Button clearSearchTextButton) { + clearSearchTextButton.requestFocus(); + searchText.setText(""); + searchText.requestFocus(); + Log.d(LOG, "Trying to show soft keyboard."); + final InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + manager.showSoftInput(searchText, InputMethodManager.SHOW_IMPLICIT); + } + + void updateLangButton() { + langButton.setText(index.shortName.toUpperCase()); + } + + void onLanguageButton() { + if (currentSearchOperation != null) { + currentSearchOperation.interrupted.set(true); + currentSearchOperation = null; + } + + indexIndex = (indexIndex + 1) % dictionary.indices.size(); + index = dictionary.indices.get(indexIndex); + indexAdapter = new IndexAdapter(index); + Log.d(LOG, "onLanguageButton, newLang=" + index.longName); + setListAdapter(indexAdapter); + updateLangButton(); + onSearchTextChange(searchText.getText().toString()); + } + + void onUpDownButton(final boolean up) { + final int firstVisibleRow = getListView().getFirstVisiblePosition(); + final RowBase row = index.rows.get(firstVisibleRow); + final TokenRow tokenRow = row.getTokenRow(true); + final int destIndexEntry; + if (up) { + if (row != tokenRow) { + destIndexEntry = tokenRow.referenceIndex; + } else { + destIndexEntry = Math.max(tokenRow.referenceIndex - 1, 0); + } + } else { + // Down + destIndexEntry = Math.min(tokenRow.referenceIndex + 1, index.sortedIndexEntries.size()); + } + final Index.IndexEntry dest = index.sortedIndexEntries.get(destIndexEntry); + Log.d(LOG, "onUpDownButton, destIndexEntry=" + dest.token); + searchText.removeTextChangedListener(searchTextWatcher); + searchText.setText(dest.token); + jumpToRow(index.sortedIndexEntries.get(destIndexEntry).startRow); + searchText.addTextChangedListener(searchTextWatcher); + } + + // -------------------------------------------------------------------------- + // Options Menu + // -------------------------------------------------------------------------- + + @Override + public boolean onCreateOptionsMenu(final Menu menu) { + + { + final MenuItem preferences = menu.add(getString(R.string.preferences)); + preferences.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(final MenuItem menuItem) { + startActivity(new Intent(DictionaryActivity.this, + PreferenceActivity.class)); + return false; + } + }); + } + + { + final MenuItem dictionaryList = menu.add(getString(R.string.dictionaryList)); + dictionaryList.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(final MenuItem menuItem) { + startActivity(DictionaryListActivity.getIntent(DictionaryActivity.this)); + finish(); + return false; + } + }); + } + + { + final MenuItem dictionaryEdit = menu.add(getString(R.string.editDictionary)); + dictionaryEdit.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(final MenuItem menuItem) { + final Intent intent = DictionaryEditActivity.getIntent(dictIndex); + startActivity(intent); + return false; + } + }); + } + + return true; + } + + + // -------------------------------------------------------------------------- + // Context Menu + clicks + // -------------------------------------------------------------------------- + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + AdapterContextMenuInfo adapterContextMenuInfo = (AdapterContextMenuInfo) menuInfo; + final RowBase row = (RowBase) getListAdapter().getItem(adapterContextMenuInfo.position); + + final MenuItem addToWordlist = menu.add(getString(R.string.addToWordList, wordList.getName())); + addToWordlist.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + onAppendToWordList(row); + return false; + } + }); + + final MenuItem copy = menu.add(android.R.string.copy); + copy.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + onCopy(row); + return false; + } + }); + + } + + @Override + protected void onListItemClick(ListView l, View v, int row, long id) { + openContextMenu(v); + } + + void onAppendToWordList(final RowBase row) { + final StringBuilder rawText = new StringBuilder(); + rawText.append( + new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").format(new Date())) + .append("\t"); + rawText.append(index.longName).append("\t"); + rawText.append(row.getTokenRow(true).getToken()).append("\t"); + rawText.append(row.getRawText(saveOnlyFirstSubentry)); + Log.d(LOG, "Writing : " + rawText); + try { + wordList.getParentFile().mkdirs(); + final PrintWriter out = new PrintWriter( + new FileWriter(wordList, true)); + out.println(rawText.toString()); + out.close(); + } catch (IOException e) { + Log.e(LOG, "Unable to append to " + wordList.getAbsolutePath(), e); + Toast.makeText(this, getString(R.string.failedAddingToWordList, wordList.getAbsolutePath()), Toast.LENGTH_LONG); + } + return; + } + + void onCopy(final RowBase row) { + Log.d(LOG, "Copy, row=" + row); + final StringBuilder result = new StringBuilder(); + result.append(row.getRawText(false)); + final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + clipboardManager.setText(result.toString()); + Log.d(LOG, "Copied: " + result); + } + + @Override + public boolean onKeyDown(final int keyCode, final KeyEvent event) { + if (event.getUnicodeChar() != 0) { + if (!searchText.hasFocus()) { + searchText.setText("" + (char) event.getUnicodeChar()); + onSearchTextChange(searchText.getText().toString()); + searchText.requestFocus(); + } + return true; + } + if (keyCode == KeyEvent.KEYCODE_BACK) { + Log.d(LOG, "Clearing dictionary prefs."); + DictionaryActivity.clearDictionaryPrefs(this); + } + return super.onKeyDown(keyCode, event); + } + + + // -------------------------------------------------------------------------- + // SearchOperation + // -------------------------------------------------------------------------- + + private void searchFinished(final SearchOperation searchOperation) { + if (searchOperation.interrupted.get()) { + Log.d(LOG, "Search operation was interrupted: " + searchOperation); + return; + } + if (searchOperation != this.currentSearchOperation) { + Log.d(LOG, "Stale searchOperation finished: " + searchOperation); + return; + } + + final Index.IndexEntry searchResult = searchOperation.searchResult; + Log.d(LOG, "searchFinished: " + searchOperation + ", searchResult=" + searchResult); + + currentSearchOperation = null; + + uiHandler.postDelayed(new Runnable() { + @Override + public void run() { + if (currentSearchOperation == null) { + jumpToRow(searchResult.startRow); + } else { + Log.d(LOG, "More coming, waiting for currentSearchOperation."); + } + } + }, 50); + +// if (!searchResult.success) { +// if (vibrator != null) { +// vibrator.vibrate(VIBRATE_MILLIS); +// } +// searchText.setText(searchResult.longestPrefixString); +// searchText.setSelection(searchResult.longestPrefixString.length()); +// return; +// } + + } + + private final void jumpToRow(final int row) { + setSelection(row); + getListView().setSelected(true); + } + + final class SearchOperation implements Runnable { + + final AtomicBoolean interrupted = new AtomicBoolean(false); + final String searchText; + final Index index; + + long searchStartMillis; + + Index.IndexEntry searchResult; + + boolean done = false; + + SearchOperation(final String searchText, final Index index) { + this.searchText = searchText.trim(); + this.index = index; + } + + public String toString() { + return String.format("SearchOperation(%s,%s)", searchText, interrupted.toString()); + } + + @Override + public void run() { + try { + searchStartMillis = System.currentTimeMillis(); + searchResult = index.findInsertionPoint(searchText, interrupted); + Log.d(LOG, "searchText=" + searchText + ", searchDuration=" + + (System.currentTimeMillis() - searchStartMillis) + ", interrupted=" + + interrupted.get()); + if (!interrupted.get()) { + uiHandler.post(new Runnable() { + @Override + public void run() { + searchFinished(SearchOperation.this); + } + }); + } + } finally { + synchronized (this) { + done = true; + this.notifyAll(); + } + } + } + } + + + // -------------------------------------------------------------------------- + // IndexAdapter + // -------------------------------------------------------------------------- + + static final class IndexAdapter extends BaseAdapter { + + final Index index; + + IndexAdapter(final Index index) { + this.index = index; + } + + @Override + public int getCount() { + return index.rows.size(); + } + + @Override + public RowBase getItem(int position) { + return index.rows.get(position); + } + + @Override + public long getItemId(int position) { + return getItem(position).index(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final RowBase row = index.rows.get(position); + if (row instanceof PairEntry.Row) { + return getView((PairEntry.Row) row, parent); + } else if (row instanceof TokenRow) { + return getView((TokenRow) row, parent); + } else { + throw new IllegalArgumentException("Unsupported Row type: " + row.getClass()); + } + } + + private View getView(PairEntry.Row row, ViewGroup parent) { + final TableLayout result = new TableLayout(parent.getContext()); + final PairEntry entry = row.getEntry(); + final int rowCount = entry.pairs.size(); + for (int r = 0; r < rowCount; ++r) { + final TableRow tableRow = new TableRow(result.getContext()); + + TextView column1 = new TextView(tableRow.getContext()); + TextView column2 = new TextView(tableRow.getContext()); + final TableRow.LayoutParams layoutParams = new TableRow.LayoutParams(); + layoutParams.weight = 0.5f; + + if (r > 0) { + final TextView spacer = new TextView(tableRow.getContext()); + spacer.setText(" • "); + tableRow.addView(spacer); + } + tableRow.addView(column1, layoutParams); + if (r > 0) { + final TextView spacer = new TextView(tableRow.getContext()); + spacer.setText(" • "); + tableRow.addView(spacer); + } + tableRow.addView(column2, layoutParams); + + column1.setWidth(1); + column2.setWidth(1); + + // TODO: color words by gender + final Pair pair = entry.pairs.get(r); + final String col1Text = index.swapPairEntries ? pair.lang2 : pair.lang1; + column1.setText(col1Text, TextView.BufferType.SPANNABLE); + final Spannable col1Spannable = (Spannable) column1.getText(); + + int startPos = 0; + final String token = row.getTokenRow(true).getToken(); + while ((startPos = col1Text.indexOf(token, startPos)) != -1) { + col1Spannable.setSpan(new StyleSpan(Typeface.BOLD), startPos, + startPos + token.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + startPos += token.length(); + } + + final String col2Text = index.swapPairEntries ? pair.lang1 : pair.lang2; + column2.setText(col2Text, TextView.BufferType.NORMAL); + + result.addView(tableRow); + } + + return result; + } + + private View getView(TokenRow row, ViewGroup parent) { + final TextView textView = new TextView(parent.getContext()); + textView.setText(row.getToken()); + textView.setTextSize(20); + return textView; + } + + } + + // -------------------------------------------------------------------------- + // SearchText + // -------------------------------------------------------------------------- + + void onSearchTextChange(final String text) { + if (dictRaf == null) { + Log.d(LOG, "searchText changed during shutdown, doing nothing."); + return; + } + if (!searchText.isFocused()) { + Log.d(LOG, "searchText changed without focus, doing nothing."); + return; + } + Log.d(LOG, "onSearchTextChange: " + text); + if (currentSearchOperation != null) { + Log.d(LOG, "Interrupting currentSearchOperation."); + currentSearchOperation.interrupted.set(true); + } + currentSearchOperation = new SearchOperation(text, index); + searchExecutor.execute(currentSearchOperation); + } + + private class SearchTextWatcher implements TextWatcher { + public void afterTextChanged(final Editable searchTextEditable) { + if (searchText.hasFocus()) { + Log.d(LOG, "Search text changed with focus: " + searchText.getText()); + // If they were typing to cause the change, update the UI. + onSearchTextChange(searchText.getText().toString()); + } + } + + public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, + int arg3) { + } + + public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { + } + } + +}