]> gitweb.fperrin.net Git - Dictionary.git/blob - src/com/hughes/android/dictionary/DictionaryActivity.java
go
[Dictionary.git] / src / com / hughes / android / dictionary / DictionaryActivity.java
1 package com.hughes.android.dictionary;
2
3 import java.io.File;
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;
11
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.View;
30 import android.view.ViewGroup;
31 import android.view.ContextMenu.ContextMenuInfo;
32 import android.view.MenuItem.OnMenuItemClickListener;
33 import android.view.View.OnClickListener;
34 import android.widget.AdapterView;
35 import android.widget.BaseAdapter;
36 import android.widget.Button;
37 import android.widget.EditText;
38 import android.widget.ListView;
39 import android.widget.TableLayout;
40 import android.widget.TableRow;
41 import android.widget.TextView;
42 import android.widget.AdapterView.OnItemLongClickListener;
43 import android.widget.AdapterView.OnItemSelectedListener;
44
45 import com.hughes.android.dictionary.Dictionary.IndexEntry;
46 import com.hughes.android.dictionary.Dictionary.LanguageData;
47 import com.hughes.android.dictionary.Dictionary.Row;
48
49 public class DictionaryActivity extends ListActivity {
50   
51   static final Intent aboutIntent = new Intent().setClassName(AboutActivity.class.getPackage().getName(), AboutActivity.class.getCanonicalName());
52   static final Intent preferencesIntent = new Intent().setClassName(PreferenceActivity.class.getPackage().getName(), PreferenceActivity.class.getCanonicalName());
53
54   private final Handler uiHandler = new Handler();
55   private final Executor searchExecutor = Executors.newSingleThreadExecutor();
56   private final DictionaryListAdapter dictionaryListAdapter = new DictionaryListAdapter();
57
58   // Never null.
59   private File wordList = new File("/sdcard/wordList.txt");
60
61   // Can be null.
62   private File dictFile = null;
63   private RandomAccessFile dictRaf = null;
64   private Dictionary dictionary = null;
65   private LanguageData activeLangaugeData = null;
66
67   private SearchOperation searchOperation = null;
68   private int selectedRowIndex = -1;
69   private int selectedTokenRowIndex = -1;
70   
71
72   /** Called when the activity is first created. */
73   @Override
74   public void onCreate(Bundle savedInstanceState) {
75     Log.d("THAD", "onCreate");
76     super.onCreate(savedInstanceState);
77   }
78   
79   @Override
80   public void onResume() {
81     super.onResume();
82
83     closeCurrentDictionary();
84
85     final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
86     wordList = new File(settings.getString(getResources().getString(R.string.wordListFile), wordList.getAbsolutePath()));
87     dictFile = new File(settings.getString(getResources().getString(R.string.dictFile), "/sdcard/de-en.dict"));
88     Log.d("THAD", "wordList=" + wordList);
89     Log.d("THAD", "dictFile=" + dictFile);
90
91     if (!dictFile.canRead()) {
92       return;
93     }
94     
95     try {
96       dictRaf = new RandomAccessFile(dictFile, "r");
97       dictionary = new Dictionary(dictRaf);
98       activeLangaugeData = dictionary.languageDatas[Entry.LANG1];
99     } catch (Exception e) {
100       throw new RuntimeException(e);
101     }
102
103     setContentView(R.layout.main);
104
105     getSearchText().addTextChangedListener(new DictionaryTextWatcher());
106
107     setListAdapter(dictionaryListAdapter);
108
109     // Language button.
110     final Button langButton = (Button) findViewById(R.id.LangButton);
111     langButton.setOnClickListener(new OnClickListener() {
112       public void onClick(View v) {
113         switchLanguage();
114       }});
115     updateLangButton();
116
117     final Button upButton = (Button) findViewById(R.id.UpButton);
118     upButton.setOnClickListener(new OnClickListener() {
119       public void onClick(View v) {
120         final int destRowIndex;
121         final Row tokenRow = activeLangaugeData.rows.get(selectedTokenRowIndex);
122         assert tokenRow.isToken();
123         final int prevTokenIndex = tokenRow.getIndex() - 1;
124         if (selectedRowIndex == selectedTokenRowIndex && selectedRowIndex > 0) {
125           destRowIndex = activeLangaugeData.sortedIndex.get(prevTokenIndex).startRow;
126         } else {
127           destRowIndex = selectedTokenRowIndex;
128         }
129         jumpToRow(destRowIndex);
130       }});
131     final Button downButton = (Button) findViewById(R.id.DownButton);
132     downButton.setOnClickListener(new OnClickListener() {
133       public void onClick(View v) {
134         final Row tokenRow = activeLangaugeData.rows.get(selectedTokenRowIndex);
135         assert tokenRow.isToken();
136         final int nextTokenIndex = tokenRow.getIndex() + 1;
137         final int destRowIndex;
138         if (nextTokenIndex < activeLangaugeData.sortedIndex.size()) {
139           destRowIndex = activeLangaugeData.sortedIndex.get(nextTokenIndex).startRow;
140         } else {
141           destRowIndex = activeLangaugeData.rows.size() - 1;
142         }
143         jumpToRow(destRowIndex);
144       }});
145
146     // ContextMenu.
147     registerForContextMenu(getListView());
148
149     // ItemSelectedListener.
150     getListView().setOnItemSelectedListener(new OnItemSelectedListener() {
151       public void onItemSelected(AdapterView<?> arg0, View arg1, int rowIndex,
152           long arg3) {
153         Log.d("THAD", "onItemSelected: " + rowIndex);
154         selectedRowIndex = rowIndex;
155         selectedTokenRowIndex = activeLangaugeData.getIndexEntryForRow(rowIndex).startRow;
156         updateSearchText();
157       }
158
159       public void onNothingSelected(AdapterView<?> arg0) {
160       }});
161     
162
163     // LongClickListener.
164     getListView().setOnItemLongClickListener((new OnItemLongClickListener() {
165       public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int rowIndex,
166           long arg3) {
167         selectedRowIndex = rowIndex;
168         return false;
169       }
170     }));
171
172     onSearchTextChange("");
173   }
174   
175   
176   @Override
177   public void onStop() {
178     super.onStop();
179     closeCurrentDictionary();
180   }
181
182   private void closeCurrentDictionary() {
183     dictionary = null;
184     activeLangaugeData = null;
185     try {
186       if (dictRaf != null) {
187         dictRaf.close();
188       }
189     } catch (IOException e) {
190       throw new RuntimeException(e);
191     }
192     dictRaf = null;
193   }
194   
195   public String getSelectedRowText() {
196     return activeLangaugeData.rowToString(activeLangaugeData.rows.get(selectedRowIndex));
197   }
198   
199   public EditText getSearchText() {
200     return (EditText) findViewById(R.id.SearchText);
201   }
202   
203   // ----------------------------------------------------------------
204   // OptionsMenu
205   // ----------------------------------------------------------------
206
207   private MenuItem switchLanguageMenuItem = null;
208   
209   @Override
210   public boolean onCreateOptionsMenu(final Menu menu) {
211     switchLanguageMenuItem = menu.add("Switch to language.");
212     switchLanguageMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener(){
213       public boolean onMenuItemClick(final MenuItem menuItem) {
214         switchLanguage();
215         return false;
216       }});
217
218     final MenuItem preferences = menu.add("Preferences...");
219     preferences.setOnMenuItemClickListener(new OnMenuItemClickListener(){
220       public boolean onMenuItemClick(final MenuItem menuItem) {
221         startActivity(preferencesIntent);
222         return false;
223       }});
224
225     final MenuItem about = menu.add("About...");
226     about.setOnMenuItemClickListener(new OnMenuItemClickListener(){
227       public boolean onMenuItemClick(final MenuItem menuItem) {
228         startActivity(aboutIntent);
229         return false;
230       }});
231
232     return true;
233   }
234   
235   @Override
236   public boolean onPrepareOptionsMenu(final Menu menu) {
237     if (dictionary != null) {
238       switchLanguageMenuItem.setTitle(String.format("Switch to %s", dictionary.languageDatas[Entry.otherLang(activeLangaugeData.lang)].language.symbol));
239     }
240     switchLanguageMenuItem.setEnabled(dictionary != null);
241     return super.onPrepareOptionsMenu(menu);
242   }
243
244   void switchLanguage() {
245     activeLangaugeData = dictionary.languageDatas[(activeLangaugeData == dictionary.languageDatas[0]) ? 1 : 0];
246     selectedRowIndex = 0;
247     selectedTokenRowIndex = 0;
248     updateLangButton();
249     dictionaryListAdapter.notifyDataSetChanged();
250     onSearchTextChange(getSearchText().getText().toString());
251   }
252   
253   void updateLangButton() {
254     final Button langButton = (Button) findViewById(R.id.LangButton);
255     langButton.setText(activeLangaugeData.language.symbol);
256   }
257   
258   // ----------------------------------------------------------------
259   // ContextMenu
260   // ----------------------------------------------------------------
261   
262   @Override
263   public void onCreateContextMenu(ContextMenu menu, View v,
264       ContextMenuInfo menuInfo) {
265     if (selectedRowIndex == -1) {
266       return;
267     }
268     final MenuItem addToWordlist = menu.add("Add to wordlist: " + wordList.getName());
269     addToWordlist.setOnMenuItemClickListener(new OnMenuItemClickListener() {
270       public boolean onMenuItemClick(MenuItem item) {
271         final String rawText = getSelectedRowText();
272         Log.d("THAD", "Writing : " + rawText);
273         try {
274           final OutputStream out = new FileOutputStream(wordList, true);
275           out.write((rawText + "\n").getBytes());
276           out.close();
277         } catch (IOException e) {
278           final AlertDialog alert = new AlertDialog.Builder(DictionaryActivity.this).create();
279           alert.setMessage("Failed to append to file: " + wordList.getAbsolutePath());
280           alert.show();
281         }
282         return false;
283       }
284     });
285   }
286   
287   @Override
288   public boolean onKeyDown(int keyCode, KeyEvent event) {
289     if (event.getUnicodeChar() != 0) {
290       final EditText searchText = getSearchText();
291       if (!searchText.hasFocus()) {
292         searchText.setText("" + (char)event.getUnicodeChar());
293         onSearchTextChange(searchText.getText().toString());
294         searchText.requestFocus();
295       }
296       return true;
297     }
298     return super.onKeyDown(keyCode, event);
299   }
300
301   @Override
302   protected void onListItemClick(ListView l, View v, int row, long id) {
303     selectedRowIndex = row;
304     Log.d("THAD", "Clicked: " + getSelectedRowText());
305     openContextMenu(getListView());
306   }
307
308   void onSearchTextChange(final String searchText) {
309     Log.d("THAD", "onSearchTextChange: " + searchText);
310     if (searchOperation != null) {
311       searchOperation.interrupted.set(true);
312     }
313     searchOperation = new SearchOperation(searchText);
314     searchExecutor.execute(searchOperation);
315   }
316   
317   private void jumpToRow(final int rowIndex) {
318     Log.d("THAD", "jumpToRow: " + rowIndex);
319     selectedRowIndex = rowIndex;
320     selectedTokenRowIndex = activeLangaugeData.getIndexEntryForRow(rowIndex).startRow;
321     getListView().setSelection(rowIndex);
322     getListView().setSelected(true);  // TODO: is this doing anything?
323     updateSearchText();
324   }
325
326   private void updateSearchText() {
327     final EditText searchText = getSearchText();
328     if (!searchText.hasFocus()) {
329       // TODO: Not so nice:
330       final String word = activeLangaugeData.getIndexEntryForRow(selectedRowIndex).word;
331       if (!word.equals(searchText.getText().toString())) {
332         Log.d("THAD", "updateSearchText: setText: " + word);
333         searchText.setText(word);
334       }
335     }
336   }
337
338   private final class SearchOperation implements Runnable {
339     final String searchText;
340     final AtomicBoolean interrupted = new AtomicBoolean(false);
341
342     public SearchOperation(final String searchText) {
343       this.searchText = searchText;
344     }
345
346     public void run() {
347       Log.d("THAD", "SearchOperation: " + searchText);
348       final int indexLocation = activeLangaugeData.lookup(searchText, interrupted);
349       if (interrupted.get()) {
350         return;
351       }
352       final IndexEntry indexEntry = activeLangaugeData.sortedIndex
353           .get(indexLocation);
354       uiHandler.post(new Runnable() {
355         public void run() {
356           jumpToRow(indexEntry.startRow);
357         }
358       });
359     }
360   }
361
362   private class DictionaryListAdapter extends BaseAdapter {
363
364     public int getCount() {
365       return dictionary != null ? activeLangaugeData.rows.size() : 0;
366     }
367
368     public Dictionary.Row getItem(int rowIndex) {
369       assert rowIndex < activeLangaugeData.rows.size();
370       return activeLangaugeData.rows.get(rowIndex);
371     }
372
373     public long getItemId(int rowIndex) {
374       return rowIndex;
375     }
376
377     public View getView(final int rowIndex, final View convertView,
378         final ViewGroup parent) {
379       final Row row = getItem(rowIndex);
380       
381       // Token row.
382       if (row.isToken()) {
383         TextView result = null;
384         if (convertView instanceof TextView) {
385           result = (TextView) convertView;
386         } else {
387           result = new TextView(parent.getContext());
388         }
389         result.setText(activeLangaugeData.rowToString(row));
390         result.setTextAppearance(parent.getContext(),
391             android.R.style.TextAppearance_Large);
392         result.setClickable(false);
393         return result;
394       }
395
396       // Entry row(s).
397       final TableLayout result = new TableLayout(parent.getContext());
398
399       final Entry entry = dictionary.entries.get(row.getIndex());
400       final int rowCount = entry.getRowCount();
401       for (int r = 0; r < rowCount; ++r) {
402         final TableRow tableRow = new TableRow(result.getContext());
403         
404         TextView column1 = new TextView(tableRow.getContext());
405         TextView column2 = new TextView(tableRow.getContext());
406         final TableRow.LayoutParams layoutParams = new TableRow.LayoutParams();
407         layoutParams.weight = 0.5f;
408         
409         if (r>0){
410           final TextView spacer = new TextView(tableRow.getContext());
411           spacer.setText(r == 0 ? "\95 " : " \95 ");
412           tableRow.addView(spacer);
413         }
414         tableRow.addView(column1, layoutParams);
415         if (r>0){
416           final TextView spacer = new TextView(tableRow.getContext());
417           spacer.setText(r == 0 ? "\95 " : " \95 ");
418           tableRow.addView(spacer);
419         }
420         tableRow.addView(column2, layoutParams);
421         
422         column1.setWidth(1);
423         column2.setWidth(1);
424         // column1.setTextAppearance(parent.getContext(), android.R.style.Text);
425         
426         // TODO: highlight query word in entries.
427         final String col1Text = entry.getAllText(activeLangaugeData.lang)[r]; 
428         column1.setText(col1Text, TextView.BufferType.SPANNABLE);
429         final Spannable col1Spannable = (Spannable) column1.getText();
430         int startPos = 0;
431         final String token = activeLangaugeData.getIndexEntryForRow(rowIndex).word;
432         while ((startPos = col1Text.indexOf(token, startPos)) != -1) {
433           col1Spannable.setSpan(new StyleSpan(Typeface.BOLD), startPos, startPos + token.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
434          startPos += token.length();
435         }
436         
437         column2.setText(entry.getAllText(Entry.otherLang(activeLangaugeData.lang))[r], TextView.BufferType.NORMAL);
438         
439         result.addView(tableRow);
440       }
441       
442       return result;
443     }
444   }  // DictionaryListAdapter
445
446   private class DictionaryTextWatcher implements TextWatcher {
447     public void afterTextChanged(Editable searchText) {
448       if (getSearchText().hasFocus()) {
449         onSearchTextChange(searchText.toString());
450       }
451     }
452
453     public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
454         int arg3) {
455     }
456
457     public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
458     }
459   }
460
461 }