]> gitweb.fperrin.net Git - Dictionary.git/blob - src/com/hughes/android/dictionary/DictionaryActivity.java
1.6
[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.FileWriter;
5 import java.io.IOException;
6 import java.io.PrintWriter;
7 import java.io.RandomAccessFile;
8 import java.text.SimpleDateFormat;
9 import java.util.Date;
10 import java.util.concurrent.Executor;
11 import java.util.concurrent.Executors;
12 import java.util.concurrent.atomic.AtomicBoolean;
13
14 import android.app.ListActivity;
15 import android.content.Context;
16 import android.content.Intent;
17 import android.content.SharedPreferences;
18 import android.content.SharedPreferences.Editor;
19 import android.graphics.Typeface;
20 import android.os.Bundle;
21 import android.os.Handler;
22 import android.preference.PreferenceManager;
23 import android.text.ClipboardManager;
24 import android.text.Editable;
25 import android.text.Spannable;
26 import android.text.TextWatcher;
27 import android.text.style.StyleSpan;
28 import android.util.Log;
29 import android.view.ContextMenu;
30 import android.view.KeyEvent;
31 import android.view.Menu;
32 import android.view.MenuItem;
33 import android.view.View;
34 import android.view.ViewGroup;
35 import android.view.ContextMenu.ContextMenuInfo;
36 import android.view.MenuItem.OnMenuItemClickListener;
37 import android.view.View.OnClickListener;
38 import android.widget.AdapterView;
39 import android.widget.BaseAdapter;
40 import android.widget.Button;
41 import android.widget.EditText;
42 import android.widget.ListView;
43 import android.widget.TableLayout;
44 import android.widget.TableRow;
45 import android.widget.TextView;
46 import android.widget.Toast;
47
48 import com.hughes.android.dictionary.Dictionary.IndexEntry;
49 import com.hughes.android.dictionary.Dictionary.LanguageData;
50 import com.hughes.android.dictionary.Dictionary.Row;
51
52 public class DictionaryActivity extends ListActivity {
53   
54   // TODO:
55   // * Download latest dicts.
56   //   * http://ftp.tu-chemnitz.de/pub/Local/urz/ding/de-en-devel/
57   //   * http://www1.dict.cc/translation_file_request.php?l=e
58   // * Compress all the strings everywhere, put compression table in file.
59   // Done:
60   // * Only one way to way for current search to end. (won't do).
61
62   static final String LOG = "QuickDic";
63   static final String PREF_DICT_ACTIVE_LANG = "DICT_DIR_PREF";
64   static final String PREF_ACTIVE_SEARCH_TEXT = "ACTIVE_WORD_PREF";
65
66   // package for test.
67   final Handler uiHandler = new Handler();
68   private final Executor searchExecutor = Executors.newSingleThreadExecutor();
69
70   EditText searchText;
71   Button langButton;
72   int lastSelectedRow = 0;  // TODO: I'm evil.
73
74   private boolean prefsMightHaveChanged = true;
75
76   // Never null.
77   private File wordList;
78   private RandomAccessFile dictRaf = null;
79   private Dictionary dictionary = null;
80   private boolean saveOnlyFirstSubentry = false;
81
82   // Visible for testing.
83   LanguageListAdapter languageList = null;
84   private SearchOperation searchOperation = null;
85
86   /** Called when the activity is first created. */
87   @Override
88   public void onCreate(Bundle savedInstanceState) {
89     super.onCreate(savedInstanceState);
90     Log.d(LOG, "onCreate:" + this);
91
92     if (Language.EN.sortCollator.compare("pre-print", "preppy") >= 0) {
93       Log
94           .e(LOG,
95               Language.EN.sortCollator.getClass() + " is buggy, lookups may not work properly.");
96     }
97
98     try {
99       initDictionaryAndPrefs();
100     } catch (Exception e) {
101       return;
102     }
103
104     // UI init.
105
106     setContentView(R.layout.main);
107     searchText = (EditText) findViewById(R.id.SearchText);
108     langButton = (Button) findViewById(R.id.LangButton);
109     
110     Log.d(LOG, "adding text changed listener");
111     searchText.addTextChangedListener(new SearchTextWatcher());
112     
113     getListView().setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
114       public void onItemSelected(AdapterView<?> arg0, View arg1, int row,
115           long arg3) {
116         setSelectedRow(row);
117       }
118       public void onNothingSelected(AdapterView<?> arg0) {
119       }
120     });
121     
122     getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
123       public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int row,
124           long arg3) {
125         setSelectedRow(row);
126         return false;
127       }
128     });
129     
130     final Button clearSearchTextButton = (Button) findViewById(R.id.ClearSearchTextButton);
131     clearSearchTextButton.setOnClickListener(new OnClickListener() {
132       public void onClick(View v) {
133         clearSearchTextButton.requestFocus();
134         searchText.setText("");
135         searchText.requestFocus();
136       }
137     });
138     clearSearchTextButton.setVisibility(PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
139         getString(R.string.showClearSearchTextButtonKey), true) ? View.VISIBLE
140         : View.GONE);
141     
142     final Button langButton = (Button) findViewById(R.id.LangButton);
143     langButton.setOnClickListener(new OnClickListener() {
144       public void onClick(View v) {
145         onLanguageButton();
146       }
147     });
148     
149     final Button upButton = (Button) findViewById(R.id.UpButton);
150     upButton.setOnClickListener(new OnClickListener() {
151       public void onClick(View v) {
152         onUpButton();
153       }
154     });
155     final Button downButton = (Button) findViewById(R.id.DownButton);
156     downButton.setOnClickListener(new OnClickListener() {
157       public void onClick(View v) {
158         onDownButton();
159       }
160     });
161
162     // ContextMenu.
163     registerForContextMenu(getListView());
164
165     updateLangButton();
166   }
167   
168   private void initDictionaryAndPrefs() throws Exception {
169     if (!prefsMightHaveChanged) {
170       return;
171     }
172     closeCurrentDictionary();
173     
174     final SharedPreferences prefs = PreferenceManager
175         .getDefaultSharedPreferences(this);
176     wordList = new File(prefs.getString(getString(R.string.wordListFileKey),
177         getString(R.string.wordListFileDefault)));
178     Log.d(LOG, "wordList=" + wordList);
179     
180     saveOnlyFirstSubentry = prefs.getBoolean(getString(R.string.saveOnlyFirstSubentryKey), false);
181
182     final File dictFile = new File(prefs.getString(getString(R.string.dictFileKey),
183         getString(R.string.dictFileDefault)));
184     Log.d(LOG, "dictFile=" + dictFile);
185     
186     try {
187     if (!dictFile.canRead()) {
188       throw new IOException("Unable to read dictionary file.");
189     }
190     
191     dictRaf = new RandomAccessFile(dictFile, "r");
192     dictionary = new Dictionary(dictRaf);
193     } catch (IOException e) {
194       Log.e(LOG, "Couldn't open dictionary.", e);
195       this.startActivity(new Intent(this, NoDictionaryActivity.class));
196       finish();
197       throw new Exception(e);
198     }
199     
200     final byte lang = prefs.getInt(PREF_DICT_ACTIVE_LANG, Entry.LANG1) == Entry.LANG1 ? Entry.LANG1
201         : Entry.LANG2;
202     
203     languageList = new LanguageListAdapter(dictionary.languageDatas[lang]);
204     setListAdapter(languageList);
205     prefsMightHaveChanged = false;
206   }
207
208   @Override
209   public void onResume() {
210     super.onResume();
211     Log.d(LOG, "onResume:" + this);
212
213     try {
214       initDictionaryAndPrefs();
215     } catch (Exception e) {
216       return;
217     }
218     
219     final SharedPreferences prefs = PreferenceManager
220         .getDefaultSharedPreferences(this);
221     final String searchTextString = prefs
222         .getString(PREF_ACTIVE_SEARCH_TEXT, "");
223     searchText.setText(searchTextString);
224     getListView().requestFocus();
225     onSearchTextChange(searchTextString);
226   }
227
228   @Override
229   public void onPause() {
230     super.onPause();
231     Log.d(LOG, "onPause:" + this);
232     final Editor prefs = PreferenceManager.getDefaultSharedPreferences(this)
233         .edit();
234     prefs.putInt(PREF_DICT_ACTIVE_LANG, languageList.languageData.lang);
235     prefs.putString(PREF_ACTIVE_SEARCH_TEXT, searchText.getText().toString());
236     prefs.commit();
237   }
238
239   @Override
240   public void onStop() {
241     super.onStop();
242     Log.d(LOG, "onStop:" + this);
243     if (isFinishing()) {
244       Log.i(LOG, "isFinishing()==true, closing dictionary.");
245       closeCurrentDictionary();
246     }
247   }
248
249   private void closeCurrentDictionary() {
250     Log.i(LOG, "closeCurrentDictionary");
251     waitForSearchEnd();
252     languageList = null;
253     setListAdapter(null);
254     dictionary = null;
255     try {
256       if (dictRaf != null) {
257         dictRaf.close();
258       }
259     } catch (IOException e) {
260       throw new RuntimeException(e);
261     }
262     dictRaf = null;
263   }
264
265   public String getSelectedRowRawText(final boolean onlyFirstSubentry) {
266     final Row row = languageList.languageData.rows.get(getSelectedRow());
267     return languageList.languageData.rowToString(row, onlyFirstSubentry);
268   }
269
270   // ----------------------------------------------------------------
271   // OptionsMenu
272   // ----------------------------------------------------------------
273
274   private MenuItem switchLanguageMenuItem = null;
275
276   @Override
277   public boolean onCreateOptionsMenu(final Menu menu) {
278     switchLanguageMenuItem = menu.add(getString(R.string.switchToLanguage));
279     switchLanguageMenuItem
280         .setOnMenuItemClickListener(new OnMenuItemClickListener() {
281           public boolean onMenuItemClick(final MenuItem menuItem) {
282             onLanguageButton();
283             return false;
284           }
285         });
286
287     final MenuItem preferences = menu.add(getString(R.string.preferences));
288     preferences.setOnMenuItemClickListener(new OnMenuItemClickListener() {
289       public boolean onMenuItemClick(final MenuItem menuItem) {
290         prefsMightHaveChanged = true;
291         startActivity(new Intent(DictionaryActivity.this,
292             PreferenceActivity.class));
293         return false;
294       }
295     });
296
297     final MenuItem about = menu.add(getString(R.string.about));
298     about.setOnMenuItemClickListener(new OnMenuItemClickListener() {
299       public boolean onMenuItemClick(final MenuItem menuItem) {
300         final Intent intent = new Intent().setClassName(AboutActivity.class
301             .getPackage().getName(), AboutActivity.class.getCanonicalName());
302         final String currentDictInfo;
303         if (dictionary == null) {
304           currentDictInfo = getString(R.string.noDictLoaded);
305         } else {
306           final LanguageData lang0 = dictionary.languageDatas[0];
307           final LanguageData lang1 = dictionary.languageDatas[1];
308           currentDictInfo = getString(R.string.aboutText, dictionary.dictionaryInfo, dictionary.entries.size(), 
309               lang0.language.symbol, lang0.sortedIndex.size(), lang0.rows.size(),
310               lang1.language.symbol, lang1.sortedIndex.size(), lang1.rows.size());
311         }
312         intent.putExtra(AboutActivity.CURRENT_DICT_INFO, currentDictInfo
313             .toString());
314         startActivity(intent);
315         return false;
316       }
317     });
318
319     final MenuItem download = menu.add(getString(R.string.downloadDictionary));
320     download.setOnMenuItemClickListener(new OnMenuItemClickListener() {
321       public boolean onMenuItemClick(final MenuItem menuItem) {
322         prefsMightHaveChanged = true;
323         startDownloadDictActivity(DictionaryActivity.this);
324         return false;
325       }
326     });
327
328     return true;
329   }
330
331   @Override
332   public boolean onPrepareOptionsMenu(final Menu menu) {
333     switchLanguageMenuItem.setTitle(getString(R.string.switchToLanguage,
334         dictionary.languageDatas[Entry
335             .otherLang(languageList.languageData.lang)].language.symbol));
336     return super.onPrepareOptionsMenu(menu);
337   }
338   
339   void updateLangButton() {
340     langButton.setText(languageList.languageData.language.symbol);
341   }
342
343   // ----------------------------------------------------------------
344   // Event handlers.
345   // ----------------------------------------------------------------
346   
347   void onLanguageButton() {
348     waitForSearchEnd();
349     languageList = new LanguageListAdapter(
350         dictionary.languageDatas[(languageList.languageData == dictionary.languageDatas[0]) ? 1
351             : 0]);
352     Log.d(LOG, "onLanguageButton, newLang=" + languageList.languageData.language.symbol);
353     setListAdapter(languageList);
354     updateLangButton();
355     onSearchTextChange(searchText.getText().toString());
356   }
357
358   void onUpButton() {
359     final int destRowIndex = languageList.languageData.getPrevTokenRow(getSelectedRow());
360     Log.d(LOG, "onUpButton, destRowIndex=" + destRowIndex);
361     jumpToRow(languageList, destRowIndex);
362   }
363
364   void onDownButton() {
365     final int destRowIndex = languageList.languageData.getNextTokenRow(getSelectedRow());
366     Log.d(LOG, "onDownButton, destRowIndex=" + destRowIndex);
367     jumpToRow(languageList, destRowIndex);
368   }
369
370   void onAppendToWordList() {
371     final int row = getSelectedRow();
372     if (row < 0) {
373       return;
374     }
375     final StringBuilder rawText = new StringBuilder();
376     final String word = languageList.languageData.getIndexEntryForRow(row).word;
377     rawText.append(
378         new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").format(new Date()))
379         .append("\t");
380     rawText.append(word).append("\t");
381     rawText.append(getSelectedRowRawText(saveOnlyFirstSubentry));
382     Log.d(LOG, "Writing : " + rawText);
383     try {
384       wordList.getParentFile().mkdirs();
385       final PrintWriter out = new PrintWriter(
386           new FileWriter(wordList, true));
387       out.println(rawText.toString());
388       out.close();
389     } catch (IOException e) {
390       Log.e(LOG, "Unable to append to " + wordList.getAbsolutePath(), e);
391       Toast.makeText(this, getString(R.string.failedAddingToWordList, wordList.getAbsolutePath()), Toast.LENGTH_LONG);
392     }
393     return;
394   }
395
396   void onCopy() {
397     final int row = getSelectedRow();
398     if (row < 0) {
399       return;
400     }
401     Log.d(LOG, "Copy." + DictionaryActivity.this.getSelectedRow());
402     final StringBuilder result = new StringBuilder();
403     result.append(getSelectedRowRawText(false));
404     final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
405     clipboardManager.setText(result.toString());
406     Log.d(LOG, "Copied: " + result);
407   }
408
409   @Override
410   public boolean onKeyDown(int keyCode, KeyEvent event) {
411     if (event.getUnicodeChar() != 0) {
412       if (!searchText.hasFocus()) {
413         searchText.setText("" + (char) event.getUnicodeChar());
414         onSearchTextChange(searchText.getText().toString());
415         searchText.requestFocus();
416       }
417       return true;
418     }
419     return super.onKeyDown(keyCode, event);
420   }
421
422   @Override
423   protected void onListItemClick(ListView l, View v, int row, long id) {
424     setSelectedRow(row);
425     openContextMenu(getListView());
426   }
427
428   void onSearchTextChange(final String searchText) {
429     Log.d(LOG, "onSearchTextChange: " + searchText);
430     synchronized (this) {
431       searchOperation = new SearchOperation(languageList, searchText, searchOperation);
432       searchExecutor.execute(searchOperation);
433     }
434   }
435
436   
437
438   // ----------------------------------------------------------------
439   // ContextMenu
440   // ----------------------------------------------------------------
441
442   @Override
443   public void onCreateContextMenu(ContextMenu menu, View v,
444       ContextMenuInfo menuInfo) {
445     final int row = getSelectedRow();
446     if (row < 0) {
447       return;
448     }
449
450     final MenuItem addToWordlist = menu.add(getString(R.string.addToWordList, wordList.getName()));
451     addToWordlist.setOnMenuItemClickListener(new OnMenuItemClickListener() {
452       public boolean onMenuItemClick(MenuItem item) {
453         onAppendToWordList();
454         return false;
455       }
456     });
457
458     final MenuItem copy = menu.add(android.R.string.copy);
459     copy.setOnMenuItemClickListener(new OnMenuItemClickListener() {
460       public boolean onMenuItemClick(MenuItem item) {
461         onCopy();
462         return false;
463       }
464     });
465
466   }
467
468   private void jumpToRow(final LanguageListAdapter dictionaryListAdapter,
469       final int rowIndex) {
470     Log.d(LOG, "jumpToRow: " + rowIndex);
471     if (dictionaryListAdapter != this.languageList) {
472       Log.w(LOG, "skipping jumpToRow for old list adapter: " + rowIndex);
473       return;
474     }
475     setSelection(rowIndex);
476     setSelectedRow(rowIndex);
477     getListView().setSelected(true);
478   }
479   
480   // TODO: delete me somehow.
481   private int getSelectedRow() {
482     return lastSelectedRow;
483   }
484   private void setSelectedRow(final int row) {
485     lastSelectedRow = row;
486     Log.d(LOG, "Selected: " + getSelectedRowRawText(true));
487     updateSearchText();
488   }
489
490   private void updateSearchText() {
491     Log.d(LOG, "updateSearchText");
492     final int selectedRowIndex = getSelectedRow();
493     if (!searchText.hasFocus()) {
494       if (selectedRowIndex >= 0) {
495         final String word = languageList.languageData
496             .getIndexEntryForRow(selectedRowIndex).word;
497         if (!word.equals(searchText.getText().toString())) {
498           Log.d(LOG, "updateSearchText: setText: " + word);
499           searchText.setText(word);
500         }
501       } else {
502         Log.w(LOG, "updateSearchText: nothing selected.");
503       }
504     }
505   }
506
507   static void startDownloadDictActivity(final Context context) {
508     final Intent intent = new Intent(context, DownloadActivity.class);
509     final SharedPreferences prefs = PreferenceManager
510         .getDefaultSharedPreferences(context);
511     final String dictFetchUrl = prefs.getString(context
512         .getString(R.string.dictFetchUrlKey), context
513         .getString(R.string.dictFetchUrlDefault));
514     final String dictFileName = prefs.getString(context
515         .getString(R.string.dictFileKey), context
516         .getString(R.string.dictFileDefault));
517     intent.putExtra(DownloadActivity.SOURCE, dictFetchUrl);
518     intent.putExtra(DownloadActivity.DEST, dictFileName);
519     context.startActivity(intent);
520   }
521
522   class LanguageListAdapter extends BaseAdapter {
523
524     // Visible for testing.
525     final LanguageData languageData;
526
527     LanguageListAdapter(final LanguageData languageData) {
528       this.languageData = languageData;
529     }
530
531     public int getCount() {
532       return languageData.rows.size();
533     }
534
535     public Dictionary.Row getItem(int rowIndex) {
536       assert rowIndex < languageData.rows.size();
537       return languageData.rows.get(rowIndex);
538     }
539
540     public long getItemId(int rowIndex) {
541       return rowIndex;
542     }
543
544     public View getView(final int rowIndex, final View convertView,
545         final ViewGroup parent) {
546       final Row row = getItem(rowIndex);
547
548       // Token row.
549       if (row.isToken()) {
550         TextView result = null;
551         if (convertView instanceof TextView) {
552           result = (TextView) convertView;
553         } else {
554           result = new TextView(parent.getContext());
555         }
556         if (row == null) {
557           return result;
558         }
559         result.setText(languageData.rowToString(row, false));
560         result.setTextAppearance(parent.getContext(),
561             android.R.style.TextAppearance_Large);
562         result.setClickable(false);
563         return result;
564       }
565
566       // Entry row(s).
567       final TableLayout result = new TableLayout(parent.getContext());
568
569       final Entry entry = dictionary.entries.get(row.getIndex());
570       final int rowCount = entry.getRowCount();
571       for (int r = 0; r < rowCount; ++r) {
572         final TableRow tableRow = new TableRow(result.getContext());
573
574         TextView column1 = new TextView(tableRow.getContext());
575         TextView column2 = new TextView(tableRow.getContext());
576         final TableRow.LayoutParams layoutParams = new TableRow.LayoutParams();
577         layoutParams.weight = 0.5f;
578
579         if (r > 0) {
580           final TextView spacer = new TextView(tableRow.getContext());
581           spacer.setText(r == 0 ? "\95 " : " \95 ");
582           tableRow.addView(spacer);
583         }
584         tableRow.addView(column1, layoutParams);
585         if (r > 0) {
586           final TextView spacer = new TextView(tableRow.getContext());
587           spacer.setText(r == 0 ? "\95 " : " \95 ");
588           tableRow.addView(spacer);
589         }
590         tableRow.addView(column2, layoutParams);
591
592         column1.setWidth(1);
593         column2.setWidth(1);
594         // column1.setTextAppearance(parent.getContext(), android.R.style.Text);
595
596         // TODO: color words by gender
597         final String col1Text = entry.getAllText(languageData.lang)[r];
598         column1.setText(col1Text, TextView.BufferType.SPANNABLE);
599         final Spannable col1Spannable = (Spannable) column1.getText();
600         int startPos = 0;
601         final String token = languageData.getIndexEntryForRow(rowIndex).word;
602         while ((startPos = col1Text.indexOf(token, startPos)) != -1) {
603           col1Spannable.setSpan(new StyleSpan(Typeface.BOLD), startPos,
604               startPos + token.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
605           startPos += token.length();
606         }
607
608         column2.setText(
609             entry.getAllText(Entry.otherLang(languageData.lang))[r],
610             TextView.BufferType.NORMAL);
611
612         result.addView(tableRow);
613       }
614
615       return result;
616     }
617
618   } // DictionaryListAdapter
619
620   private final class SearchOperation implements Runnable {
621     SearchOperation previousSearchOperation;
622     
623     final LanguageListAdapter listAdapter;
624     final LanguageData languageData;
625     final String searchText;
626     final AtomicBoolean interrupted = new AtomicBoolean(false);
627     boolean searchFinished = false;
628
629     SearchOperation(final LanguageListAdapter listAdapter,
630         final String searchText, final SearchOperation previousSearchOperation) {
631       this.listAdapter = listAdapter;
632       this.languageData = listAdapter.languageData;
633       this.searchText = searchText;
634       this.previousSearchOperation = previousSearchOperation;
635     }
636
637     public void run() {
638       if (previousSearchOperation != null) {
639         previousSearchOperation.stopAndWait();
640       }
641       previousSearchOperation = null;
642       
643       Log.d(LOG, "SearchOperation: " + searchText);
644       final int indexLocation = languageData.lookup(searchText, interrupted);
645       if (!interrupted.get()) {
646         final IndexEntry indexEntry = languageData.sortedIndex.get(indexLocation);
647         
648         Log.d(LOG, "SearchOperation completed: " + indexEntry.toString());
649         uiHandler.post(new Runnable() {
650           public void run() {
651             // Check is just a performance operation.
652             if (!interrupted.get()) {
653               // This is safe, because it checks that the listAdapter hasn't changed.
654               jumpToRow(listAdapter, indexEntry.startRow);
655             }
656             synchronized (DictionaryActivity.this) {
657               searchOperation = null;
658               DictionaryActivity.this.notifyAll();
659             }
660           }
661         });
662       }      
663       synchronized (this) {
664         searchFinished = true;
665         this.notifyAll();
666       }
667     }
668     
669     private void stopAndWait() {
670       interrupted.set(true);
671       synchronized (this) {
672         while (!searchFinished) {
673           Log.d(LOG, "stopAndWait: " + searchText);
674           try {
675             this.wait();
676           } catch (InterruptedException e) {
677             Log.e(LOG, "Interrupted", e);
678           }
679         }
680       }
681     }
682   }  // SearchOperation
683
684   void waitForSearchEnd() {
685     synchronized (this) {
686       while (searchOperation != null) {
687         try {
688           this.wait();
689         } catch (InterruptedException e) {
690           Log.e(LOG, "Interrupted.", e);
691         }
692       }
693     }
694   }
695
696   private class SearchTextWatcher implements TextWatcher {
697     public void afterTextChanged(final Editable searchTextEditable) {
698       Log.d(LOG, "Search text changed: " + searchText.getText().toString());
699       if (searchText.hasFocus()) {
700         // If they were typing to cause the change, update the UI.
701         onSearchTextChange(searchText.getText().toString());
702       }
703     }
704
705     public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
706         int arg3) {
707     }
708
709     public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
710     }
711   }
712
713 }