]> gitweb.fperrin.net Git - Dictionary.git/blob - src/com/hughes/android/dictionary/Dictionary.java
go
[Dictionary.git] / src / com / hughes / android / dictionary / Dictionary.java
1 package com.hughes.android.dictionary;
2
3 import java.io.File;
4 import java.io.FileNotFoundException;
5 import java.io.FileOutputStream;
6 import java.io.IOException;
7 import java.io.OutputStream;
8 import java.io.RandomAccessFile;
9 import java.util.ArrayList;
10 import java.util.Collections;
11 import java.util.LinkedHashSet;
12 import java.util.List;
13 import java.util.Set;
14 import java.util.concurrent.Executor;
15 import java.util.concurrent.Executors;
16 import java.util.concurrent.atomic.AtomicBoolean;
17
18 import android.app.AlertDialog;
19 import android.app.ListActivity;
20 import android.os.Bundle;
21 import android.os.Handler;
22 import android.text.Editable;
23 import android.text.TextWatcher;
24 import android.util.Log;
25 import android.view.ContextMenu;
26 import android.view.MenuItem;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.view.ContextMenu.ContextMenuInfo;
30 import android.view.MenuItem.OnMenuItemClickListener;
31 import android.widget.AdapterView;
32 import android.widget.BaseAdapter;
33 import android.widget.EditText;
34 import android.widget.ListView;
35 import android.widget.TextView;
36 import android.widget.AdapterView.OnItemLongClickListener;
37
38 import com.hughes.util.FileUtil;
39
40 public class Dictionary extends ListActivity {
41
42   public static final String INDEX_FORMAT = "%s_index_%d";
43   private File dictionaryFile = new File("/sdcard/dict-de-en.txt");
44   private File wordList = new File("/sdcard/wordList.txt");
45
46   private RandomAccessFile dictionaryRaf;
47   private final Index[] indexes = new Index[2];
48   private final byte lang = Entry.LANG1;
49
50   final Handler uiHandler = new Handler();
51
52   private final Object mutex = new Object();
53   private Executor searchExecutor = Executors.newSingleThreadExecutor();
54   private SearchOperation searchOperation = null;
55   private List<Entry> entries = Collections.emptyList();
56   private DictionaryListAdapter dictionaryListAdapter = new DictionaryListAdapter();
57   private int selectedItem = -1;
58
59   /** Called when the activity is first created. */
60   @Override
61   public void onCreate(Bundle savedInstanceState) {
62     Log.d("THAD", "onCreate");
63     super.onCreate(savedInstanceState);
64     setContentView(R.layout.main);
65
66     EditText searchText = (EditText) findViewById(R.id.SearchText);
67     searchText.addTextChangedListener(new DictionaryTextWatcher());
68
69     setListAdapter(dictionaryListAdapter);
70
71     try {
72       loadIndex();
73     } catch (Exception e) {
74       throw new RuntimeException(e);
75     }
76     onSearchTextChange("");
77
78     registerForContextMenu(getListView());
79     getListView().setOnItemLongClickListener((new OnItemLongClickListener() {
80       public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2,
81           long arg3) {
82         selectedItem = arg2;
83         return false;
84       }
85     }));
86   }
87
88   private void loadIndex() throws IOException, ClassNotFoundException {
89     Log.d("THAD", "enter loadIndex");
90     indexes[0] = new Index(String.format(INDEX_FORMAT, dictionaryFile
91         .getAbsolutePath(), Entry.LANG1));
92     dictionaryRaf = new RandomAccessFile(dictionaryFile, "r");
93     Log.d("THAD", "exit loadIndex");
94   }
95
96   @Override
97   public void onCreateContextMenu(ContextMenu menu, View v,
98       ContextMenuInfo menuInfo) {
99     if (selectedItem == -1) {
100       return;
101     }
102     final MenuItem addToWordlist = menu.add("Add to wordlist.");
103     addToWordlist.setOnMenuItemClickListener(new OnMenuItemClickListener() {
104       public boolean onMenuItemClick(MenuItem item) {
105         final String rawText = entries.get(selectedItem).getRawText();
106         Log.d("THAD", "Writing : "
107                 + rawText);
108         try {
109           final OutputStream out = new FileOutputStream(wordList, true);
110           out.write((rawText + "\n").getBytes());
111           out.close();
112         } catch (IOException e) {
113           throw new RuntimeException(e);
114         }
115         return false;
116       }
117     });
118   }
119
120   @Override
121   protected void onListItemClick(ListView l, View v, int position, long id) {
122     Log.d("THAD", "Clicked: " + entries.get(position).getRawText());
123     selectedItem = position;
124     openContextMenu(getListView());
125   }
126
127   void onSearchTextChange(final String searchText) {
128     Log.d("THAD", "onSearchTextChange: " + searchText);
129     synchronized (mutex) {
130       if (searchOperation != null) {
131         searchOperation.interrupted.set(true);
132       }
133       searchOperation = new SearchOperation(searchText);
134     }
135     searchExecutor.execute(searchOperation);
136   }
137
138   private final class SearchOperation implements Runnable {
139     final String searchText;
140     final int count = 100;
141     final AtomicBoolean interrupted = new AtomicBoolean(false);
142
143     public SearchOperation(final String searchText) {
144       this.searchText = searchText.toLowerCase(); // TODO: better
145     }
146
147     public void run() {
148       Log.d("THAD", "SearchOperation: " + searchText);
149       final List<Entry> newEntries = new ArrayList<Entry>(count);
150       try {
151         final Index.Node node = indexes[lang].lookup(searchText);
152         final Set<Integer> entryOffsets = new LinkedHashSet<Integer>(count);
153         node.getDescendantEntryOffsets(entryOffsets, count);
154         for (final int entryOffset : entryOffsets) {
155           if (interrupted.get()) {
156             Log.d("THAD", "Interrupted while parsing entries.");
157             return;
158           }
159           final String line = FileUtil.readLine(dictionaryRaf, entryOffset);
160           final Entry entry = new Entry(line);
161           newEntries.add(entry);
162         }
163       } catch (IOException e) {
164         Log.e("THAD", "Search failure.", e);
165       }
166
167       synchronized (mutex) {
168         if (interrupted.get()) {
169           return;
170         }
171         entries = newEntries;
172       }
173
174       uiHandler.post(new Runnable() {
175         public void run() {
176           synchronized (mutex) {
177             dictionaryListAdapter.notifyDataSetChanged();
178             if (entries.size() > 0) {
179               setSelection(0);
180             }
181           }
182         }
183       });
184     }
185   }
186
187   private class DictionaryListAdapter extends BaseAdapter {
188
189     public int getCount() {
190       synchronized (mutex) {
191         return entries.size();
192       }
193     }
194
195     public Object getItem(int position) {
196       synchronized (mutex) {
197         assert position < entries.size();
198         return entries.get(position).getFormattedEntry(lang);
199       }
200     }
201
202     public long getItemId(int position) {
203       return position;
204     }
205
206     public View getView(final int position, final View convertView,
207         final ViewGroup parent) {
208       TextView result = null;
209       if (convertView instanceof TextView) {
210         result = (TextView) convertView;
211       } else {
212         result = new TextView(parent.getContext());
213       }
214       result.setText((String) getItem(position));
215       return result;
216     }
217
218   }
219
220   private class DictionaryTextWatcher implements TextWatcher {
221     public void afterTextChanged(Editable searchText) {
222       onSearchTextChange(searchText.toString());
223     }
224
225     public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
226         int arg3) {
227     }
228
229     public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
230     }
231   }
232
233 }