]> 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.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;
45
46 import com.hughes.android.dictionary.Dictionary.IndexEntry;
47 import com.hughes.android.dictionary.Dictionary.LanguageData;
48 import com.hughes.android.dictionary.Dictionary.Row;
49
50 public class DictionaryActivity extends ListActivity {
51   
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());
54
55   private final Handler uiHandler = new Handler();
56   private final Executor searchExecutor = Executors.newSingleThreadExecutor();
57   private final DictionaryListAdapter dictionaryListAdapter = new DictionaryListAdapter();
58
59   // Never null.
60   private File wordList = new File("/sdcard/wordList.txt");
61
62   // Can be null.
63   private File dictFile = null;
64   private RandomAccessFile dictRaf = null;
65   private Dictionary dictionary = null;
66   private LanguageData activeLangaugeData = null;
67
68   private SearchOperation searchOperation = null;
69   private int selectedRowIndex = -1;
70   private int selectedTokenRowIndex = -1;
71   
72
73   /** Called when the activity is first created. */
74   @Override
75   public void onCreate(Bundle savedInstanceState) {
76     Log.d("THAD", "onCreate");
77     super.onCreate(savedInstanceState);
78   }
79   
80   @Override
81   public void onResume() {
82     super.onResume();
83
84     closeCurrentDictionary();
85
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);
91
92     if (!dictFile.canRead()) {
93       return;
94     }
95     
96     try {
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);
102     }
103
104     setContentView(R.layout.main);
105
106     getSearchText().addTextChangedListener(new DictionaryTextWatcher());
107
108     setListAdapter(dictionaryListAdapter);
109
110     // Language button.
111     final Button langButton = (Button) findViewById(R.id.LangButton);
112     langButton.setOnClickListener(new OnClickListener() {
113       public void onClick(View v) {
114         switchLanguage();
115       }});
116     updateLangButton();
117
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;
127         } else {
128           destRowIndex = selectedTokenRowIndex;
129         }
130         jumpToRow(destRowIndex);
131       }});
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;
141         } else {
142           destRowIndex = activeLangaugeData.rows.size() - 1;
143         }
144         jumpToRow(destRowIndex);
145       }});
146
147     // ContextMenu.
148     registerForContextMenu(getListView());
149
150     // ItemSelectedListener.
151     getListView().setOnItemSelectedListener(new OnItemSelectedListener() {
152       public void onItemSelected(AdapterView<?> arg0, View arg1, int rowIndex,
153           long arg3) {
154         Log.d("THAD", "onItemSelected: " + rowIndex);
155         selectedRowIndex = rowIndex;
156         selectedTokenRowIndex = activeLangaugeData.getIndexEntryForRow(rowIndex).startRow;
157         updateSearchText();
158       }
159
160       public void onNothingSelected(AdapterView<?> arg0) {
161       }});
162     
163
164     // LongClickListener.
165     getListView().setOnItemLongClickListener((new OnItemLongClickListener() {
166       public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int rowIndex,
167           long arg3) {
168         selectedRowIndex = rowIndex;
169         return false;
170       }
171     }));
172
173     onSearchTextChange("");
174   }
175   
176   
177   @Override
178   public void onStop() {
179     super.onStop();
180     closeCurrentDictionary();
181   }
182
183   private void closeCurrentDictionary() {
184     dictionary = null;
185     activeLangaugeData = null;
186     try {
187       if (dictRaf != null) {
188         dictRaf.close();
189       }
190     } catch (IOException e) {
191       throw new RuntimeException(e);
192     }
193     dictRaf = null;
194   }
195   
196   public String getSelectedRowText() {
197     return activeLangaugeData.rowToString(activeLangaugeData.rows.get(selectedRowIndex));
198   }
199   
200   public EditText getSearchText() {
201     return (EditText) findViewById(R.id.SearchText);
202   }
203   
204   // ----------------------------------------------------------------
205   // OptionsMenu
206   // ----------------------------------------------------------------
207
208   private MenuItem switchLanguageMenuItem = null;
209   
210
211   @Override
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) {
216         switchLanguage();
217         return false;
218       }});
219
220     final MenuItem preferences = menu.add("Preferences...");
221     preferences.setOnMenuItemClickListener(new OnMenuItemClickListener(){
222       public boolean onMenuItemClick(final MenuItem menuItem) {
223         startActivity(preferencesIntent);
224         return false;
225       }});
226
227     final MenuItem about = menu.add("About...");
228     about.setOnMenuItemClickListener(new OnMenuItemClickListener(){
229       public boolean onMenuItemClick(final MenuItem menuItem) {
230         startActivity(aboutIntent);
231         return false;
232       }});
233
234     return true;
235   }
236   
237   @Override
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));
241     }
242     switchLanguageMenuItem.setEnabled(dictionary != null);
243     return super.onPrepareOptionsMenu(menu);
244   }
245
246   void switchLanguage() {
247     activeLangaugeData = dictionary.languageDatas[(activeLangaugeData == dictionary.languageDatas[0]) ? 1 : 0];
248     selectedRowIndex = 0;
249     selectedTokenRowIndex = 0;
250     updateLangButton();
251     dictionaryListAdapter.notifyDataSetChanged();
252     onSearchTextChange(getSearchText().getText().toString());
253   }
254   
255   void updateLangButton() {
256     final Button langButton = (Button) findViewById(R.id.LangButton);
257     langButton.setText(activeLangaugeData.language.symbol);
258   }
259   
260   // ----------------------------------------------------------------
261   // ContextMenu
262   // ----------------------------------------------------------------
263   
264   @Override
265   public void onCreateContextMenu(ContextMenu menu, View v,
266       ContextMenuInfo menuInfo) {
267     if (selectedRowIndex == -1) {
268       return;
269     }
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);
275         try {
276           final OutputStream out = new FileOutputStream(wordList, true);
277           out.write((rawText + "\n").getBytes());
278           out.close();
279         } catch (IOException e) {
280           final AlertDialog alert = new AlertDialog.Builder(DictionaryActivity.this).create();
281           alert.setMessage("Failed to append to file: " + wordList.getAbsolutePath());
282           alert.show();
283         }
284         return false;
285       }
286     });
287   }
288   
289   @Override
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();
297       }
298       return true;
299     }
300     return super.onKeyDown(keyCode, event);
301   }
302
303   @Override
304   protected void onListItemClick(ListView l, View v, int row, long id) {
305     selectedRowIndex = row;
306     Log.d("THAD", "Clicked: " + getSelectedRowText());
307     openContextMenu(getListView());
308   }
309
310   void onSearchTextChange(final String searchText) {
311     Log.d("THAD", "onSearchTextChange: " + searchText);
312     if (searchOperation != null) {
313       searchOperation.interrupted.set(true);
314     }
315     searchOperation = new SearchOperation(searchText);
316     searchExecutor.execute(searchOperation);
317   }
318   
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?
325     updateSearchText();
326   }
327
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);
335       }
336     }
337   }
338
339   private final class SearchOperation implements Runnable {
340     final String searchText;
341     final AtomicBoolean interrupted = new AtomicBoolean(false);
342
343     public SearchOperation(final String searchText) {
344       this.searchText = searchText;
345     }
346
347     public void run() {
348       Log.d("THAD", "SearchOperation: " + searchText);
349       final int indexLocation = activeLangaugeData.lookup(searchText, interrupted);
350       if (interrupted.get()) {
351         return;
352       }
353       final IndexEntry indexEntry = activeLangaugeData.sortedIndex
354           .get(indexLocation);
355       uiHandler.post(new Runnable() {
356         public void run() {
357           jumpToRow(indexEntry.startRow);
358         }
359       });
360     }
361   }
362
363   private class DictionaryListAdapter extends BaseAdapter {
364
365     public int getCount() {
366       return dictionary != null ? activeLangaugeData.rows.size() : 0;
367     }
368
369     public Dictionary.Row getItem(int rowIndex) {
370       assert rowIndex < activeLangaugeData.rows.size();
371       return activeLangaugeData.rows.get(rowIndex);
372     }
373
374     public long getItemId(int rowIndex) {
375       return rowIndex;
376     }
377
378     public View getView(final int rowIndex, final View convertView,
379         final ViewGroup parent) {
380       final Row row = getItem(rowIndex);
381       
382       // Token row.
383       if (row.isToken()) {
384         TextView result = null;
385         if (convertView instanceof TextView) {
386           result = (TextView) convertView;
387         } else {
388           result = new TextView(parent.getContext());
389         }
390         result.setText(activeLangaugeData.rowToString(row));
391         result.setTextAppearance(parent.getContext(),
392             android.R.style.TextAppearance_Large);
393         result.setClickable(false);
394         return result;
395       }
396
397       // Entry row(s).
398       final TableLayout result = new TableLayout(parent.getContext());
399
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());
404         
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;
409         
410         if (r>0){
411           final TextView spacer = new TextView(tableRow.getContext());
412           spacer.setText(r == 0 ? "\95 " : " \95 ");
413           tableRow.addView(spacer);
414         }
415         tableRow.addView(column1, layoutParams);
416         if (r>0){
417           final TextView spacer = new TextView(tableRow.getContext());
418           spacer.setText(r == 0 ? "\95 " : " \95 ");
419           tableRow.addView(spacer);
420         }
421         tableRow.addView(column2, layoutParams);
422         
423         column1.setWidth(1);
424         column2.setWidth(1);
425         // column1.setTextAppearance(parent.getContext(), android.R.style.Text);
426         
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();
431         int startPos = 0;
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();
436         }
437         
438         column2.setText(entry.getAllText(Entry.otherLang(activeLangaugeData.lang))[r], TextView.BufferType.NORMAL);
439         
440         result.addView(tableRow);
441       }
442       
443       return result;
444     }
445   }  // DictionaryListAdapter
446
447   private class DictionaryTextWatcher implements TextWatcher {
448     public void afterTextChanged(Editable searchText) {
449       if (getSearchText().hasFocus()) {
450         onSearchTextChange(searchText.toString());
451       }
452     }
453
454     public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
455         int arg3) {
456     }
457
458     public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
459     }
460   }
461
462 }