1 package com.hughes.android.dictionary;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 import java.io.OutputStream;
7 import java.io.RandomAccessFile;
8 import java.util.concurrent.Executor;
9 import java.util.concurrent.Executors;
10 import java.util.concurrent.atomic.AtomicBoolean;
12 import android.app.AlertDialog;
13 import android.app.ListActivity;
14 import android.content.Intent;
15 import android.content.SharedPreferences;
16 import android.graphics.Typeface;
17 import android.os.Bundle;
18 import android.os.Handler;
19 import android.preference.PreferenceManager;
20 import android.text.Editable;
21 import android.text.Spannable;
22 import android.text.TextWatcher;
23 import android.text.style.StyleSpan;
24 import android.util.Log;
25 import android.view.ContextMenu;
26 import android.view.KeyEvent;
27 import android.view.Menu;
28 import android.view.MenuItem;
29 import android.view.MotionEvent;
30 import android.view.View;
31 import android.view.ViewGroup;
32 import android.view.ContextMenu.ContextMenuInfo;
33 import android.view.MenuItem.OnMenuItemClickListener;
34 import android.view.View.OnClickListener;
35 import android.widget.AdapterView;
36 import android.widget.BaseAdapter;
37 import android.widget.Button;
38 import android.widget.EditText;
39 import android.widget.ListView;
40 import android.widget.TableLayout;
41 import android.widget.TableRow;
42 import android.widget.TextView;
43 import android.widget.AdapterView.OnItemLongClickListener;
44 import android.widget.AdapterView.OnItemSelectedListener;
46 import com.hughes.android.dictionary.Dictionary.IndexEntry;
47 import com.hughes.android.dictionary.Dictionary.LanguageData;
48 import com.hughes.android.dictionary.Dictionary.Row;
50 public class DictionaryActivity extends ListActivity {
52 static final Intent aboutIntent = new Intent().setClassName(AboutActivity.class.getPackage().getName(), AboutActivity.class.getCanonicalName());
53 static final Intent preferencesIntent = new Intent().setClassName(PreferenceActivity.class.getPackage().getName(), PreferenceActivity.class.getCanonicalName());
55 private final Handler uiHandler = new Handler();
56 private final Executor searchExecutor = Executors.newSingleThreadExecutor();
57 private final DictionaryListAdapter dictionaryListAdapter = new DictionaryListAdapter();
60 private File wordList = new File("/sdcard/wordList.txt");
63 private File dictFile = null;
64 private RandomAccessFile dictRaf = null;
65 private Dictionary dictionary = null;
66 private LanguageData activeLangaugeData = null;
68 private SearchOperation searchOperation = null;
69 private int selectedRowIndex = -1;
70 private int selectedTokenRowIndex = -1;
73 /** Called when the activity is first created. */
75 public void onCreate(Bundle savedInstanceState) {
76 Log.d("THAD", "onCreate");
77 super.onCreate(savedInstanceState);
81 public void onResume() {
84 closeCurrentDictionary();
86 final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
87 wordList = new File(settings.getString(getResources().getString(R.string.wordListFile), wordList.getAbsolutePath()));
88 dictFile = new File(settings.getString(getResources().getString(R.string.dictFile), "/sdcard/de-en.dict"));
89 Log.d("THAD", "wordList=" + wordList);
90 Log.d("THAD", "dictFile=" + dictFile);
92 if (!dictFile.canRead()) {
97 dictRaf = new RandomAccessFile(dictFile, "r");
98 dictionary = new Dictionary(dictRaf);
99 activeLangaugeData = dictionary.languageDatas[Entry.LANG1];
100 } catch (Exception e) {
101 throw new RuntimeException(e);
104 setContentView(R.layout.main);
106 getSearchText().addTextChangedListener(new DictionaryTextWatcher());
108 setListAdapter(dictionaryListAdapter);
111 final Button langButton = (Button) findViewById(R.id.LangButton);
112 langButton.setOnClickListener(new OnClickListener() {
113 public void onClick(View v) {
118 final Button upButton = (Button) findViewById(R.id.UpButton);
119 upButton.setOnClickListener(new OnClickListener() {
120 public void onClick(View v) {
121 final int destRowIndex;
122 final Row tokenRow = activeLangaugeData.rows.get(selectedTokenRowIndex);
123 assert tokenRow.isToken();
124 final int prevTokenIndex = tokenRow.getIndex() - 1;
125 if (selectedRowIndex == selectedTokenRowIndex && selectedRowIndex > 0) {
126 destRowIndex = activeLangaugeData.sortedIndex.get(prevTokenIndex).startRow;
128 destRowIndex = selectedTokenRowIndex;
130 jumpToRow(destRowIndex);
132 final Button downButton = (Button) findViewById(R.id.DownButton);
133 downButton.setOnClickListener(new OnClickListener() {
134 public void onClick(View v) {
135 final Row tokenRow = activeLangaugeData.rows.get(selectedTokenRowIndex);
136 assert tokenRow.isToken();
137 final int nextTokenIndex = tokenRow.getIndex() + 1;
138 final int destRowIndex;
139 if (nextTokenIndex < activeLangaugeData.sortedIndex.size()) {
140 destRowIndex = activeLangaugeData.sortedIndex.get(nextTokenIndex).startRow;
142 destRowIndex = activeLangaugeData.rows.size() - 1;
144 jumpToRow(destRowIndex);
148 registerForContextMenu(getListView());
150 // ItemSelectedListener.
151 getListView().setOnItemSelectedListener(new OnItemSelectedListener() {
152 public void onItemSelected(AdapterView<?> arg0, View arg1, int rowIndex,
154 Log.d("THAD", "onItemSelected: " + rowIndex);
155 selectedRowIndex = rowIndex;
156 selectedTokenRowIndex = activeLangaugeData.getIndexEntryForRow(rowIndex).startRow;
160 public void onNothingSelected(AdapterView<?> arg0) {
164 // LongClickListener.
165 getListView().setOnItemLongClickListener((new OnItemLongClickListener() {
166 public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int rowIndex,
168 selectedRowIndex = rowIndex;
173 onSearchTextChange("");
178 public void onStop() {
180 closeCurrentDictionary();
183 private void closeCurrentDictionary() {
185 activeLangaugeData = null;
187 if (dictRaf != null) {
190 } catch (IOException e) {
191 throw new RuntimeException(e);
196 public String getSelectedRowText() {
197 return activeLangaugeData.rowToString(activeLangaugeData.rows.get(selectedRowIndex));
200 public EditText getSearchText() {
201 return (EditText) findViewById(R.id.SearchText);
204 // ----------------------------------------------------------------
206 // ----------------------------------------------------------------
208 private MenuItem switchLanguageMenuItem = null;
212 public boolean onCreateOptionsMenu(final Menu menu) {
213 switchLanguageMenuItem = menu.add("Switch to language.");
214 switchLanguageMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener(){
215 public boolean onMenuItemClick(final MenuItem menuItem) {
220 final MenuItem preferences = menu.add("Preferences...");
221 preferences.setOnMenuItemClickListener(new OnMenuItemClickListener(){
222 public boolean onMenuItemClick(final MenuItem menuItem) {
223 startActivity(preferencesIntent);
227 final MenuItem about = menu.add("About...");
228 about.setOnMenuItemClickListener(new OnMenuItemClickListener(){
229 public boolean onMenuItemClick(final MenuItem menuItem) {
230 startActivity(aboutIntent);
238 public boolean onPrepareOptionsMenu(final Menu menu) {
239 if (dictionary != null) {
240 switchLanguageMenuItem.setTitle(String.format("Switch to %s", dictionary.languageDatas[Entry.otherLang(activeLangaugeData.lang)].language.symbol));
242 switchLanguageMenuItem.setEnabled(dictionary != null);
243 return super.onPrepareOptionsMenu(menu);
246 void switchLanguage() {
247 activeLangaugeData = dictionary.languageDatas[(activeLangaugeData == dictionary.languageDatas[0]) ? 1 : 0];
248 selectedRowIndex = 0;
249 selectedTokenRowIndex = 0;
251 dictionaryListAdapter.notifyDataSetChanged();
252 onSearchTextChange(getSearchText().getText().toString());
255 void updateLangButton() {
256 final Button langButton = (Button) findViewById(R.id.LangButton);
257 langButton.setText(activeLangaugeData.language.symbol);
260 // ----------------------------------------------------------------
262 // ----------------------------------------------------------------
265 public void onCreateContextMenu(ContextMenu menu, View v,
266 ContextMenuInfo menuInfo) {
267 if (selectedRowIndex == -1) {
270 final MenuItem addToWordlist = menu.add("Add to wordlist: " + wordList.getName());
271 addToWordlist.setOnMenuItemClickListener(new OnMenuItemClickListener() {
272 public boolean onMenuItemClick(MenuItem item) {
273 final String rawText = getSelectedRowText();
274 Log.d("THAD", "Writing : " + rawText);
276 final OutputStream out = new FileOutputStream(wordList, true);
277 out.write((rawText + "\n").getBytes());
279 } catch (IOException e) {
280 final AlertDialog alert = new AlertDialog.Builder(DictionaryActivity.this).create();
281 alert.setMessage("Failed to append to file: " + wordList.getAbsolutePath());
290 public boolean onKeyDown(int keyCode, KeyEvent event) {
291 if (event.getUnicodeChar() != 0) {
292 final EditText searchText = getSearchText();
293 if (!searchText.hasFocus()) {
294 searchText.setText("" + (char)event.getUnicodeChar());
295 onSearchTextChange(searchText.getText().toString());
296 searchText.requestFocus();
300 return super.onKeyDown(keyCode, event);
304 protected void onListItemClick(ListView l, View v, int row, long id) {
305 selectedRowIndex = row;
306 Log.d("THAD", "Clicked: " + getSelectedRowText());
307 openContextMenu(getListView());
310 void onSearchTextChange(final String searchText) {
311 Log.d("THAD", "onSearchTextChange: " + searchText);
312 if (searchOperation != null) {
313 searchOperation.interrupted.set(true);
315 searchOperation = new SearchOperation(searchText);
316 searchExecutor.execute(searchOperation);
319 private void jumpToRow(final int rowIndex) {
320 Log.d("THAD", "jumpToRow: " + rowIndex);
321 selectedRowIndex = rowIndex;
322 selectedTokenRowIndex = activeLangaugeData.getIndexEntryForRow(rowIndex).startRow;
323 getListView().setSelection(rowIndex);
324 getListView().setSelected(true); // TODO: is this doing anything?
328 private void updateSearchText() {
329 final EditText searchText = getSearchText();
330 if (!searchText.hasFocus()) {
331 final String word = activeLangaugeData.getIndexEntryForRow(selectedRowIndex).word;
332 if (!word.equals(searchText.getText().toString())) {
333 Log.d("THAD", "updateSearchText: setText: " + word);
334 searchText.setText(word);
339 private final class SearchOperation implements Runnable {
340 final String searchText;
341 final AtomicBoolean interrupted = new AtomicBoolean(false);
343 public SearchOperation(final String searchText) {
344 this.searchText = searchText;
348 Log.d("THAD", "SearchOperation: " + searchText);
349 final int indexLocation = activeLangaugeData.lookup(searchText, interrupted);
350 if (interrupted.get()) {
353 final IndexEntry indexEntry = activeLangaugeData.sortedIndex
355 uiHandler.post(new Runnable() {
357 jumpToRow(indexEntry.startRow);
363 private class DictionaryListAdapter extends BaseAdapter {
365 public int getCount() {
366 return dictionary != null ? activeLangaugeData.rows.size() : 0;
369 public Dictionary.Row getItem(int rowIndex) {
370 assert rowIndex < activeLangaugeData.rows.size();
371 return activeLangaugeData.rows.get(rowIndex);
374 public long getItemId(int rowIndex) {
378 public View getView(final int rowIndex, final View convertView,
379 final ViewGroup parent) {
380 final Row row = getItem(rowIndex);
384 TextView result = null;
385 if (convertView instanceof TextView) {
386 result = (TextView) convertView;
388 result = new TextView(parent.getContext());
390 result.setText(activeLangaugeData.rowToString(row));
391 result.setTextAppearance(parent.getContext(),
392 android.R.style.TextAppearance_Large);
393 result.setClickable(false);
398 final TableLayout result = new TableLayout(parent.getContext());
400 final Entry entry = dictionary.entries.get(row.getIndex());
401 final int rowCount = entry.getRowCount();
402 for (int r = 0; r < rowCount; ++r) {
403 final TableRow tableRow = new TableRow(result.getContext());
405 TextView column1 = new TextView(tableRow.getContext());
406 TextView column2 = new TextView(tableRow.getContext());
407 final TableRow.LayoutParams layoutParams = new TableRow.LayoutParams();
408 layoutParams.weight = 0.5f;
411 final TextView spacer = new TextView(tableRow.getContext());
412 spacer.setText(r == 0 ? "
\95 " : "
\95 ");
413 tableRow.addView(spacer);
415 tableRow.addView(column1, layoutParams);
417 final TextView spacer = new TextView(tableRow.getContext());
418 spacer.setText(r == 0 ? "
\95 " : "
\95 ");
419 tableRow.addView(spacer);
421 tableRow.addView(column2, layoutParams);
425 // column1.setTextAppearance(parent.getContext(), android.R.style.Text);
427 // TODO: color words by gender
428 final String col1Text = entry.getAllText(activeLangaugeData.lang)[r];
429 column1.setText(col1Text, TextView.BufferType.SPANNABLE);
430 final Spannable col1Spannable = (Spannable) column1.getText();
432 final String token = activeLangaugeData.getIndexEntryForRow(rowIndex).word;
433 while ((startPos = col1Text.indexOf(token, startPos)) != -1) {
434 col1Spannable.setSpan(new StyleSpan(Typeface.BOLD), startPos, startPos + token.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
435 startPos += token.length();
438 column2.setText(entry.getAllText(Entry.otherLang(activeLangaugeData.lang))[r], TextView.BufferType.NORMAL);
440 result.addView(tableRow);
445 } // DictionaryListAdapter
447 private class DictionaryTextWatcher implements TextWatcher {
448 public void afterTextChanged(Editable searchText) {
449 if (getSearchText().hasFocus()) {
450 onSearchTextChange(searchText.toString());
454 public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
458 public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {