1 package com.hughes.android.dictionary;
4 import java.io.IOException;
5 import java.io.RandomAccessFile;
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.LinkedHashSet;
11 import java.util.concurrent.Executor;
12 import java.util.concurrent.Executors;
13 import java.util.concurrent.atomic.AtomicBoolean;
15 import android.app.ListActivity;
16 import android.os.Bundle;
17 import android.os.Handler;
18 import android.text.Editable;
19 import android.text.TextWatcher;
20 import android.util.Log;
21 import android.view.ContextMenu;
22 import android.view.KeyEvent;
23 import android.view.MenuItem;
24 import android.view.View;
25 import android.view.ViewGroup;
26 import android.view.Window;
27 import android.view.ContextMenu.ContextMenuInfo;
28 import android.view.MenuItem.OnMenuItemClickListener;
29 import android.view.View.OnLongClickListener;
30 import android.widget.AdapterView;
31 import android.widget.BaseAdapter;
32 import android.widget.EditText;
33 import android.widget.ListView;
34 import android.widget.TextView;
35 import android.widget.AdapterView.OnItemClickListener;
36 import android.widget.AdapterView.OnItemLongClickListener;
38 import com.hughes.util.FileUtil;
40 public class Dictionary extends ListActivity {
42 public static final String INDEX_FORMAT = "%s_index_%d";
43 private File dictionaryFile = new File("/sdcard/dict-de-en.txt");
45 private RandomAccessFile dictionaryRaf;
46 private final Index[] indexes = new Index[2];
47 private final byte lang = Entry.LANG1;
49 final Handler uiHandler = new Handler();
51 private final Object mutex = new Object();
52 private Executor searchExecutor = Executors.newSingleThreadExecutor();
53 private SearchOperation searchOperation = null;
54 private List<Entry> entries = Collections.emptyList();
55 private DictionaryListAdapter dictionaryListAdapter = new DictionaryListAdapter();
56 private int selectedItem = -1;
58 /** Called when the activity is first created. */
60 public void onCreate(Bundle savedInstanceState) {
61 Log.d("THAD", "onCreate");
62 super.onCreate(savedInstanceState);
63 setContentView(R.layout.main);
65 EditText searchText = (EditText) findViewById(R.id.SearchText);
66 searchText.addTextChangedListener(new DictionaryTextWatcher());
68 setListAdapter(dictionaryListAdapter);
72 } catch (Exception e) {
73 throw new RuntimeException(e);
75 onSearchTextChange("");
77 registerForContextMenu(getListView());
78 getListView().setOnItemLongClickListener((new OnItemLongClickListener() {
79 public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2,
88 public void onCreateContextMenu(ContextMenu menu, View v,
89 ContextMenuInfo menuInfo) {
90 if (selectedItem == -1) {
93 final MenuItem addToWordlist = menu.add("Add to wordlist.");
94 addToWordlist.setOnMenuItemClickListener(new OnMenuItemClickListener() {
95 public boolean onMenuItemClick(MenuItem item) {
97 .d("THAD", "context menu: "
98 + entries.get(selectedItem).getRawText());
105 protected void onListItemClick(ListView l, View v, int position, long id) {
106 Log.d("THAD", "Clicked: " + entries.get(position).getRawText());
107 selectedItem = position;
108 openContextMenu(getListView());
111 private void loadIndex() throws IOException, ClassNotFoundException {
112 Log.d("THAD", "enter loadIndex");
113 indexes[0] = new Index(String.format(INDEX_FORMAT, dictionaryFile
114 .getAbsolutePath(), Entry.LANG1));
115 dictionaryRaf = new RandomAccessFile(dictionaryFile, "r");
116 Log.d("THAD", "exit loadIndex");
119 void onSearchTextChange(final String searchText) {
120 Log.d("THAD", "onSearchTextChange: " + searchText);
121 synchronized (mutex) {
122 if (searchOperation != null) {
123 searchOperation.interrupted.set(true);
125 searchOperation = new SearchOperation(searchText);
127 searchExecutor.execute(searchOperation);
130 private final class SearchOperation implements Runnable {
131 final String searchText;
132 final int count = 100;
133 final AtomicBoolean interrupted = new AtomicBoolean(false);
135 public SearchOperation(final String searchText) {
136 this.searchText = searchText.toLowerCase(); // TODO: better
140 Log.d("THAD", "SearchOperation: " + searchText);
141 final List<Entry> newEntries = new ArrayList<Entry>(count);
143 final Index.Node node = indexes[lang].lookup(searchText);
144 final Set<Integer> entryOffsets = new LinkedHashSet<Integer>(count);
145 node.getDescendantEntryOffsets(entryOffsets, count);
146 for (final int entryOffset : entryOffsets) {
147 if (interrupted.get()) {
148 Log.d("THAD", "Interrupted while parsing entries.");
151 final String line = FileUtil.readLine(dictionaryRaf, entryOffset);
152 final Entry entry = new Entry(line);
153 newEntries.add(entry);
155 } catch (IOException e) {
156 Log.e("THAD", "Search failure.", e);
159 synchronized (mutex) {
160 if (interrupted.get()) {
163 entries = newEntries;
166 uiHandler.post(new Runnable() {
168 synchronized (mutex) {
169 dictionaryListAdapter.notifyDataSetChanged();
170 if (entries.size() > 0) {
179 private class DictionaryListAdapter extends BaseAdapter {
181 public int getCount() {
182 synchronized (mutex) {
183 return entries.size();
187 public Object getItem(int position) {
188 synchronized (mutex) {
189 assert position < entries.size();
190 return entries.get(position).getFormattedEntry(lang);
194 public long getItemId(int position) {
198 public View getView(final int position, final View convertView,
199 final ViewGroup parent) {
200 TextView result = null;
201 if (convertView instanceof TextView) {
202 result = (TextView) convertView;
204 result = new TextView(parent.getContext());
206 result.setText((String) getItem(position));
212 private class DictionaryTextWatcher implements TextWatcher {
213 public void afterTextChanged(Editable searchText) {
214 onSearchTextChange(searchText.toString());
217 public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
221 public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
226 // TODO Auto-generated method stub