import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.text.SimpleDateFormat;
+import java.util.Arrays;
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.Context;
import android.content.Intent;
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.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
+import android.widget.Toast;
import com.hughes.android.dictionary.Dictionary.IndexEntry;
import com.hughes.android.dictionary.Dictionary.LanguageData;
import com.hughes.android.dictionary.Dictionary.Row;
+import com.ibm.icu.text.Collator;
public class DictionaryActivity extends ListActivity {
// TODO:
- // * Only have one live SearchActivity, and a way to wait for it to die.
- // * Don't destroy dict unless we're really shutting down (not on screen rotate).
- // * Move (re-)init code to a method, set a flag if prefs might have changed, invoke re-init in onResume, which clears flag and reloads prefs.
+ // * Download latest dicts.
+ // * http://ftp.tu-chemnitz.de/pub/Local/urz/ding/de-en-devel/
+ // * http://www1.dict.cc/translation_file_request.php?l=e
// * Compress all the strings everywhere, put compression table in file.
+ // Done:
+ // * Only one way to way for current search to end. (won't do).
static final String LOG = "QuickDic";
static final String PREF_DICT_ACTIVE_LANG = "DICT_DIR_PREF";
// package for test.
final Handler uiHandler = new Handler();
+ private final Executor searchExecutor = Executors.newSingleThreadExecutor();
EditText searchText;
+ Button langButton;
+ int lastSelectedRow = 0; // TODO: I'm evil.
- private final Executor searchExecutor = Executors.newSingleThreadExecutor();
+ private boolean prefsMightHaveChanged = true;
// Never null.
- private boolean prefsMightHaveChanged = true;
private File wordList;
-
private RandomAccessFile dictRaf = null;
private Dictionary dictionary = null;
+ private boolean saveOnlyFirstSubentry = false;
// Visible for testing.
LanguageListAdapter languageList = null;
-
private SearchOperation searchOperation = null;
+
+ public DictionaryActivity() {
+
+ searchExecutor.execute(new Runnable() {
+ public void run() {
+ final long startMillis = System.currentTimeMillis();
+ for (final String lang : Arrays.asList("EN", "DE")) {
+ Language.lookup(lang).getFindCollator();
+ final Collator c = Language.lookup(lang).getSortCollator();
+ 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));
+ }
+ });
- private int selectedRowIndex;
- private int selectedTokenRowIndex;
+ }
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Log.d(LOG, "onCreate");
-
- if (Language.EN.sortCollator.compare("pre-print", "preppy") >= 0) {
- Log
- .e(LOG,
- "Android java.text.Collator is buggy, lookups may not work properly.");
- }
+ Log.d(LOG, "onCreate:" + this);
- initDictionaryAndPrefs();
- if (dictRaf == null) {
+ try {
+ initDictionaryAndPrefs();
+ } catch (Exception e) {
return;
}
setContentView(R.layout.main);
searchText = (EditText) findViewById(R.id.SearchText);
-
+ langButton = (Button) findViewById(R.id.LangButton);
+
Log.d(LOG, "adding text changed listener");
searchText.addTextChangedListener(new SearchTextWatcher());
-
- // Language button.
+
+ getListView().setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ public void onItemSelected(AdapterView<?> arg0, View arg1, int row,
+ long arg3) {
+ setSelectedRow(row);
+ }
+ public void onNothingSelected(AdapterView<?> arg0) {
+ }
+ });
+
+ getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+ public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int row,
+ long arg3) {
+ setSelectedRow(row);
+ return false;
+ }
+ });
+
+ final Button clearSearchTextButton = (Button) findViewById(R.id.ClearSearchTextButton);
+ clearSearchTextButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ clearSearchTextButton.requestFocus();
+ searchText.setText("");
+ searchText.requestFocus();
+ }
+ });
+ 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();
}
});
+
final Button upButton = (Button) findViewById(R.id.UpButton);
upButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
updateLangButton();
}
-
- private void initDictionaryAndPrefs() {
+
+ private void initDictionaryAndPrefs() throws Exception {
if (!prefsMightHaveChanged) {
return;
}
wordList = new File(prefs.getString(getString(R.string.wordListFileKey),
getString(R.string.wordListFileDefault)));
Log.d(LOG, "wordList=" + wordList);
+
+ saveOnlyFirstSubentry = prefs.getBoolean(getString(R.string.saveOnlyFirstSubentryKey), false);
final File dictFile = new File(prefs.getString(getString(R.string.dictFileKey),
getString(R.string.dictFileDefault)));
Log.d(LOG, "dictFile=" + dictFile);
- if (!dictFile.canRead()) {
- Log.w(LOG, "Unable to read dictionary file.");
- this.startActivity(new Intent(this, NoDictionaryActivity.class));
- finish();
- }
-
+
try {
+ if (!dictFile.canRead()) {
+ throw new IOException("Unable to read dictionary file.");
+ }
+
dictRaf = new RandomAccessFile(dictFile, "r");
+ final long startMillis = System.currentTimeMillis();
dictionary = new Dictionary(dictRaf);
- } catch (Exception e) {
- throw new RuntimeException(e);
+ Log.d(LOG, "Read dictionary millis: " + (System.currentTimeMillis() - startMillis));
+ } catch (IOException e) {
+ Log.e(LOG, "Couldn't open dictionary.", e);
+ this.startActivity(new Intent(this, NoDictionaryActivity.class));
+ finish();
+ throw new Exception(e);
}
-
+
final byte lang = prefs.getInt(PREF_DICT_ACTIVE_LANG, Entry.LANG1) == Entry.LANG1 ? Entry.LANG1
: Entry.LANG2;
-
+
languageList = new LanguageListAdapter(dictionary.languageDatas[lang]);
setListAdapter(languageList);
prefsMightHaveChanged = false;
@Override
public void onResume() {
super.onResume();
-
- if (prefsMightHaveChanged) {
-
+ Log.d(LOG, "onResume:" + this);
+
+ try {
+ initDictionaryAndPrefs();
+ } catch (Exception e) {
+ return;
}
final SharedPreferences prefs = PreferenceManager
final String searchTextString = prefs
.getString(PREF_ACTIVE_SEARCH_TEXT, "");
searchText.setText(searchTextString);
+ getListView().requestFocus();
onSearchTextChange(searchTextString);
}
@Override
public void onPause() {
super.onPause();
+ Log.d(LOG, "onPause:" + this);
final Editor prefs = PreferenceManager.getDefaultSharedPreferences(this)
.edit();
prefs.putInt(PREF_DICT_ACTIVE_LANG, languageList.languageData.lang);
@Override
public void onStop() {
super.onStop();
+ Log.d(LOG, "onStop:" + this);
if (isFinishing()) {
+ Log.i(LOG, "isFinishing()==true, closing dictionary.");
closeCurrentDictionary();
}
}
private void closeCurrentDictionary() {
Log.i(LOG, "closeCurrentDictionary");
- if (searchOperation != null) {
- searchOperation.stopAndWait();
- searchOperation = null;
+ if (dictionary == null) {
+ return;
}
+ waitForSearchEnd();
languageList = null;
setListAdapter(null);
+ Log.d(LOG, "setListAdapter finished.");
dictionary = null;
try {
if (dictRaf != null) {
dictRaf = null;
}
- public String getSelectedRowRawText() {
- final int row = getSelectedItemPosition();
- return row < 0 ? "" : languageList.languageData
- .rowToString(languageList.languageData.rows.get(row));
+ public String getSelectedRowRawText(final boolean onlyFirstSubentry) {
+ final Row row = languageList.languageData.rows.get(getSelectedRow());
+ return languageList.languageData.rowToString(row, onlyFirstSubentry);
}
// ----------------------------------------------------------------
public boolean onMenuItemClick(final MenuItem menuItem) {
final Intent intent = new Intent().setClassName(AboutActivity.class
.getPackage().getName(), AboutActivity.class.getCanonicalName());
- final StringBuilder currentDictInfo = new StringBuilder();
+ final String currentDictInfo;
if (dictionary == null) {
- currentDictInfo.append(getString(R.string.noDictLoaded));
+ currentDictInfo = getString(R.string.noDictLoaded);
} 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");
- }
+ final LanguageData lang0 = dictionary.languageDatas[0];
+ final LanguageData lang1 = dictionary.languageDatas[1];
+ currentDictInfo = getString(R.string.aboutText, dictionary.dictionaryInfo, dictionary.entries.size(),
+ lang0.language.symbol, lang0.sortedIndex.size(), lang0.rows.size(),
+ lang1.language.symbol, lang1.sortedIndex.size(), lang1.rows.size());
}
intent.putExtra(AboutActivity.CURRENT_DICT_INFO, currentDictInfo
.toString());
final MenuItem download = menu.add(getString(R.string.downloadDictionary));
download.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(final MenuItem menuItem) {
+ prefsMightHaveChanged = true;
startDownloadDictActivity(DictionaryActivity.this);
return false;
}
@Override
public boolean onPrepareOptionsMenu(final Menu menu) {
- switchLanguageMenuItem.setTitle(String.format(
- getString(R.string.switchToLanguage), dictionary.languageDatas[Entry
+ switchLanguageMenuItem.setTitle(getString(R.string.switchToLanguage,
+ dictionary.languageDatas[Entry
.otherLang(languageList.languageData.lang)].language.symbol));
return super.onPrepareOptionsMenu(menu);
}
void updateLangButton() {
- final Button langButton = (Button) findViewById(R.id.LangButton);
langButton.setText(languageList.languageData.language.symbol);
}
// ----------------------------------------------------------------
void onLanguageButton() {
+ waitForSearchEnd();
languageList = new LanguageListAdapter(
dictionary.languageDatas[(languageList.languageData == dictionary.languageDatas[0]) ? 1
: 0]);
}
void onUpButton() {
- final int destRowIndex;
- final Row tokenRow = languageList.languageData.rows
- .get(selectedTokenRowIndex);
- assert tokenRow.isToken();
- final int prevTokenIndex = tokenRow.getIndex() - 1;
- if (selectedRowIndex == selectedTokenRowIndex && selectedRowIndex > 0) {
- destRowIndex = languageList.languageData.sortedIndex
- .get(prevTokenIndex).startRow;
- Log.d(LOG, "onUpButton, jumping back a word, destRowIndex=" + destRowIndex);
- } else {
- destRowIndex = selectedTokenRowIndex;
- Log.d(LOG, "onUpButton, jumping to top of word, destRowIndex=" + destRowIndex);
- }
+ final int destRowIndex = languageList.languageData.getPrevTokenRow(getSelectedRow());
+ Log.d(LOG, "onUpButton, destRowIndex=" + destRowIndex);
jumpToRow(languageList, destRowIndex);
}
void onDownButton() {
- final Row tokenRow = languageList.languageData.rows
- .get(selectedTokenRowIndex);
- assert tokenRow.isToken();
- final int nextTokenIndex = tokenRow.getIndex() + 1;
- final int destRowIndex;
- if (nextTokenIndex < languageList.languageData.sortedIndex.size()) {
- destRowIndex = languageList.languageData.sortedIndex
- .get(nextTokenIndex).startRow;
- Log.d(LOG, "onDownButton, jumping down a word, destRowIndex=" + destRowIndex);
- } else {
- destRowIndex = languageList.languageData.rows.size() - 1;
- Log.d(LOG, "onDownButton, jumping to end of dict, destRowIndex=" + destRowIndex);
- }
+ final int destRowIndex = languageList.languageData.getNextTokenRow(getSelectedRow());
+ Log.d(LOG, "onDownButton, destRowIndex=" + destRowIndex);
jumpToRow(languageList, destRowIndex);
}
void onAppendToWordList() {
- final int row = getSelectedItemPosition();
+ final int row = getSelectedRow();
if (row < 0) {
return;
}
new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").format(new Date()))
.append("\t");
rawText.append(word).append("\t");
- rawText.append(getSelectedRowRawText());
+ rawText.append(getSelectedRowRawText(saveOnlyFirstSubentry));
Log.d(LOG, "Writing : " + rawText);
try {
wordList.getParentFile().mkdirs();
out.close();
} catch (IOException e) {
Log.e(LOG, "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();
+ Toast.makeText(this, getString(R.string.failedAddingToWordList, wordList.getAbsolutePath()), Toast.LENGTH_LONG);
}
return;
}
void onCopy() {
- final int row = getSelectedItemPosition();
+ final int row = getSelectedRow();
if (row < 0) {
return;
}
- Log.d(LOG, "Copy." + DictionaryActivity.this.getSelectedItemPosition());
+ Log.d(LOG, "Copy." + DictionaryActivity.this.getSelectedRow());
final StringBuilder result = new StringBuilder();
- result.append(getSelectedRowRawText());
+ result.append(getSelectedRowRawText(false));
final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
clipboardManager.setText(result.toString());
Log.d(LOG, "Copied: " + result);
@Override
protected void onListItemClick(ListView l, View v, int row, long id) {
- Log.d(LOG, "Clicked: " + getSelectedRowRawText());
+ setSelectedRow(row);
openContextMenu(getListView());
}
void onSearchTextChange(final String searchText) {
Log.d(LOG, "onSearchTextChange: " + searchText);
- searchOperation = new SearchOperation(languageList, searchText, searchOperation);
- searchExecutor.execute(searchOperation);
+ synchronized (this) {
+ searchOperation = new SearchOperation(languageList, searchText, searchOperation);
+ searchExecutor.execute(searchOperation);
+ }
}
+
+
// ----------------------------------------------------------------
// ContextMenu
// ----------------------------------------------------------------
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
- final int row = getSelectedItemPosition();
+ final int row = getSelectedRow();
if (row < 0) {
return;
}
- final MenuItem addToWordlist = menu.add(String.format(
- getString(R.string.addToWordList), wordList.getName()));
+ final MenuItem addToWordlist = menu.add(getString(R.string.addToWordList, wordList.getName()));
addToWordlist.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
onAppendToWordList();
Log.w(LOG, "skipping jumpToRow for old list adapter: " + rowIndex);
return;
}
- // selectedTokenRowIndex =
- // languageList.languageData.getIndexEntryForRow(rowIndex).startRow;
setSelection(rowIndex);
- getListView().setSelected(true); // TODO: is this doing anything?
+ setSelectedRow(rowIndex);
+ getListView().setSelected(true);
+ }
+
+ // TODO: delete me somehow.
+ private int getSelectedRow() {
+ return lastSelectedRow;
+ }
+ private void setSelectedRow(final int row) {
+ lastSelectedRow = row;
+ Log.d(LOG, "Selected: " + getSelectedRowRawText(true));
updateSearchText();
}
private void updateSearchText() {
Log.d(LOG, "updateSearchText");
+ final int selectedRowIndex = getSelectedRow();
if (!searchText.hasFocus()) {
- final String word = languageList.languageData
- .getIndexEntryForRow(selectedRowIndex).word;
- if (!word.equals(searchText.getText().toString())) {
- Log.d(LOG, "updateSearchText: setText: " + word);
- searchText.setText(word);
+ if (selectedRowIndex >= 0) {
+ final String word = languageList.languageData
+ .getIndexEntryForRow(selectedRowIndex).word;
+ if (!word.equals(searchText.getText().toString())) {
+ Log.d(LOG, "updateSearchText: setText: " + word);
+ searchText.setText(word);
+ }
+ } else {
+ Log.w(LOG, "updateSearchText: nothing selected.");
}
}
}
if (row == null) {
return result;
}
- result.setText(languageData.rowToString(row));
+ result.setText(languageData.rowToString(row, false));
result.setTextAppearance(parent.getContext(),
android.R.style.TextAppearance_Large);
result.setClickable(false);
final LanguageData languageData;
final String searchText;
final AtomicBoolean interrupted = new AtomicBoolean(false);
- boolean finished = false;
+ boolean searchFinished = false;
SearchOperation(final LanguageListAdapter listAdapter,
final String searchText, final SearchOperation previousSearchOperation) {
Log.d(LOG, "SearchOperation: " + searchText);
final int indexLocation = languageData.lookup(searchText, interrupted);
- if (interrupted.get()) {
- return;
- }
- final IndexEntry indexEntry = languageData.sortedIndex.get(indexLocation);
-
- Log.d(LOG, "SearchOperation completed: " + indexEntry.toString());
- uiHandler.post(new Runnable() {
- public void run() {
- // Check is just a performance operation.
- if (!interrupted.get()) {
- // This is safe, because it checks that the listAdapter hasn't changed.
- jumpToRow(listAdapter, indexEntry.startRow);
+ if (!interrupted.get()) {
+ final IndexEntry indexEntry = languageData.sortedIndex.get(indexLocation);
+
+ Log.d(LOG, "SearchOperation completed: " + indexEntry.toString());
+ uiHandler.post(new Runnable() {
+ public void run() {
+ // Check is just a performance operation.
+ if (!interrupted.get()) {
+ // This is safe, because it checks that the listAdapter hasn't changed.
+ jumpToRow(listAdapter, indexEntry.startRow);
+ }
+ synchronized (DictionaryActivity.this) {
+ searchOperation = null;
+ DictionaryActivity.this.notifyAll();
+ }
}
- }
- });
-
+ });
+ }
synchronized (this) {
- finished = true;
+ searchFinished = true;
this.notifyAll();
}
}
- public void stopAndWait() {
+ private void stopAndWait() {
interrupted.set(true);
synchronized (this) {
- while (!finished) {
+ while (!searchFinished) {
Log.d(LOG, "stopAndWait: " + searchText);
try {
this.wait();
}
}
}
-
-
+ } // SearchOperation
+
+ void waitForSearchEnd() {
+ synchronized (this) {
+ while (searchOperation != null) {
+ Log.d(LOG, "waitForSearchEnd");
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ Log.e(LOG, "Interrupted.", e);
+ }
+ }
+ }
}
private class SearchTextWatcher implements TextWatcher {