]> 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;\r
2 \r
3 import java.io.File;\r
4 import java.io.FileWriter;\r
5 import java.io.IOException;\r
6 import java.io.PrintWriter;\r
7 import java.io.RandomAccessFile;\r
8 import java.text.SimpleDateFormat;\r
9 import java.util.Date;\r
10 import java.util.concurrent.Executor;\r
11 import java.util.concurrent.Executors;\r
12 import java.util.concurrent.atomic.AtomicBoolean;\r
13 \r
14 import android.app.ListActivity;\r
15 import android.content.Context;\r
16 import android.content.Intent;\r
17 import android.content.SharedPreferences;\r
18 import android.graphics.Typeface;\r
19 import android.os.Bundle;\r
20 import android.os.Handler;\r
21 import android.os.Vibrator;\r
22 import android.preference.PreferenceManager;\r
23 import android.text.ClipboardManager;\r
24 import android.text.Editable;\r
25 import android.text.Spannable;\r
26 import android.text.TextWatcher;\r
27 import android.text.style.StyleSpan;\r
28 import android.util.Log;\r
29 import android.view.ContextMenu;\r
30 import android.view.KeyEvent;\r
31 import android.view.Menu;\r
32 import android.view.MenuItem;\r
33 import android.view.View;\r
34 import android.view.ViewGroup;\r
35 import android.view.ContextMenu.ContextMenuInfo;\r
36 import android.view.MenuItem.OnMenuItemClickListener;\r
37 import android.view.View.OnClickListener;\r
38 import android.view.inputmethod.InputMethodManager;\r
39 import android.widget.AdapterView;\r
40 import android.widget.BaseAdapter;\r
41 import android.widget.Button;\r
42 import android.widget.EditText;\r
43 import android.widget.ListAdapter;\r
44 import android.widget.ListView;\r
45 import android.widget.TableLayout;\r
46 import android.widget.TableRow;\r
47 import android.widget.TextView;\r
48 import android.widget.Toast;\r
49 import android.widget.AdapterView.AdapterContextMenuInfo;\r
50 \r
51 import com.hughes.android.dictionary.engine.Dictionary;\r
52 import com.hughes.android.dictionary.engine.Index;\r
53 import com.hughes.android.dictionary.engine.PairEntry;\r
54 import com.hughes.android.dictionary.engine.RowBase;\r
55 import com.hughes.android.dictionary.engine.TokenRow;\r
56 import com.hughes.android.util.PersistentObjectCache;\r
57 \r
58 public class DictionaryActivity extends ListActivity {\r
59 \r
60   static final String LOG = "QuickDic";\r
61   \r
62   static final int VIBRATE_MILLIS = 100;\r
63 \r
64   int dictIndex = 0;\r
65   RandomAccessFile dictRaf = null;\r
66   Dictionary dictionary = null;\r
67   int indexIndex = 0;\r
68   Index index = null;\r
69   \r
70   // package for test.\r
71   final Handler uiHandler = new Handler();\r
72   private final Executor searchExecutor = Executors.newSingleThreadExecutor();\r
73   private SearchOperation currentSearchOperation = null;\r
74 \r
75   EditText searchText;\r
76   Button langButton;\r
77 \r
78   // Never null.\r
79   private File wordList = null;\r
80   private boolean saveOnlyFirstSubentry = false;\r
81 \r
82   // Visible for testing.\r
83   ListAdapter indexAdapter = null;\r
84 \r
85   private Vibrator vibrator = null;\r
86   \r
87   public DictionaryActivity() {\r
88   }\r
89   \r
90   public static Intent getIntent(final Context context, final int dictIndex, final int indexIndex, final String searchToken) {\r
91     setDictionaryPrefs(context, dictIndex, indexIndex, searchToken);\r
92     \r
93     final Intent intent = new Intent();\r
94     intent.setClassName(DictionaryActivity.class.getPackage().getName(), DictionaryActivity.class.getName());\r
95     return intent;\r
96   }\r
97 \r
98   public static void setDictionaryPrefs(final Context context,\r
99       final int dictIndex, final int indexIndex, final String searchToken) {\r
100     final SharedPreferences.Editor prefs = PreferenceManager.getDefaultSharedPreferences(context).edit();\r
101     prefs.putInt(C.DICT_INDEX, dictIndex);\r
102     prefs.putInt(C.INDEX_INDEX, indexIndex);\r
103     prefs.putString(C.SEARCH_TOKEN, searchToken);\r
104     prefs.commit();\r
105   }\r
106 \r
107   public static void clearDictionaryPrefs(final Context context) {\r
108     final SharedPreferences.Editor prefs = PreferenceManager.getDefaultSharedPreferences(context).edit();\r
109     prefs.remove(C.DICT_INDEX);\r
110     prefs.remove(C.INDEX_INDEX);\r
111     prefs.remove(C.SEARCH_TOKEN);\r
112     prefs.commit();\r
113   }\r
114 \r
115   @Override\r
116   public void onCreate(Bundle savedInstanceState) {\r
117     super.onCreate(savedInstanceState);\r
118     \r
119     final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);\r
120     \r
121     try {\r
122       PersistentObjectCache.init(this);\r
123       QuickDicConfig quickDicConfig = PersistentObjectCache.init(\r
124           this).read(C.DICTIONARY_CONFIGS, QuickDicConfig.class);\r
125       dictIndex = prefs.getInt(C.DICT_INDEX, 0) ;\r
126       final DictionaryConfig dictionaryConfig = quickDicConfig.dictionaryConfigs.get(dictIndex);\r
127       dictRaf = new RandomAccessFile(dictionaryConfig.localFile, "r");\r
128       dictionary = new Dictionary(dictRaf); \r
129     } catch (Exception e) {\r
130       Log.e(LOG, "Unable to load dictionary.", e);\r
131       Toast.makeText(this, getString(R.string.invalidDictionary, "", e.getMessage()), Toast.LENGTH_LONG);\r
132       startActivity(DictionaryEditActivity.getIntent(dictIndex));\r
133       finish();\r
134       return;\r
135     }\r
136 \r
137     // Pre-load the collators.\r
138     searchExecutor.execute(new Runnable() {\r
139       public void run() {\r
140         final long startMillis = System.currentTimeMillis();\r
141         for (final Index index : dictionary.indices) {\r
142           final com.ibm.icu.text.Collator c = index.sortLanguage.getCollator();\r
143           if (c.compare("pre-print", "preppy") >= 0) {\r
144             Log.e(LOG, c.getClass()\r
145                 + " is buggy, lookups may not work properly.");\r
146           }\r
147         }\r
148         Log.d(LOG, "Loading collators took:"\r
149             + (System.currentTimeMillis() - startMillis));\r
150       }\r
151     });\r
152     \r
153     indexIndex = prefs.getInt(C.INDEX_INDEX, 0) % dictionary.indices.size();\r
154     index = dictionary.indices.get(indexIndex);\r
155     setListAdapter(new IndexAdapter(index));\r
156     \r
157     setContentView(R.layout.dictionary_activity);\r
158     searchText = (EditText) findViewById(R.id.SearchText);\r
159     langButton = (Button) findViewById(R.id.LangButton);\r
160     \r
161     searchText.requestFocus();\r
162     searchText.addTextChangedListener(new SearchTextWatcher());\r
163     searchText.setText(prefs.getString(C.SEARCH_TOKEN, ""));\r
164     Log.d(LOG, "Trying to restore searchText=" + searchText.getText());\r
165     \r
166     final Button clearSearchTextButton = (Button) findViewById(R.id.ClearSearchTextButton);\r
167     clearSearchTextButton.setOnClickListener(new OnClickListener() {\r
168       public void onClick(View v) {\r
169         onClearSearchTextButton(clearSearchTextButton);\r
170       }\r
171     });\r
172     clearSearchTextButton.setVisibility(PreferenceManager.getDefaultSharedPreferences(this).getBoolean(\r
173         getString(R.string.showClearSearchTextButtonKey), true) ? View.VISIBLE\r
174         : View.GONE);\r
175     \r
176     final Button langButton = (Button) findViewById(R.id.LangButton);\r
177     langButton.setOnClickListener(new OnClickListener() {\r
178       public void onClick(View v) {\r
179         onLanguageButton();\r
180       }\r
181     });\r
182     updateLangButton();\r
183     \r
184     final Button upButton = (Button) findViewById(R.id.UpButton);\r
185     upButton.setOnClickListener(new OnClickListener() {\r
186       public void onClick(View v) {\r
187         onUpDownButton(true);\r
188       }\r
189     });\r
190     final Button downButton = (Button) findViewById(R.id.DownButton);\r
191     downButton.setOnClickListener(new OnClickListener() {\r
192       public void onClick(View v) {\r
193         onUpDownButton(false);\r
194       }\r
195     });\r
196 \r
197    getListView().setOnItemSelectedListener(new ListView.OnItemSelectedListener() {\r
198       @Override\r
199       public void onItemSelected(AdapterView<?> adapterView, View arg1, final int position,\r
200           long id) {\r
201         if (!searchText.isFocused()) {\r
202           // TODO: don't do this if multi words are entered.\r
203           final RowBase row = (RowBase) getListAdapter().getItem(position);\r
204           final TokenRow tokenRow = row.getTokenRow(true);\r
205           searchText.setText(tokenRow.getToken());\r
206         }\r
207       }\r
208 \r
209       @Override\r
210       public void onNothingSelected(AdapterView<?> arg0) {\r
211       }\r
212     });\r
213 \r
214     // ContextMenu.\r
215     registerForContextMenu(getListView());\r
216 \r
217     // Prefs.\r
218     wordList = new File(prefs.getString(getString(R.string.wordListFileKey),\r
219         getString(R.string.wordListFileDefault)));\r
220     saveOnlyFirstSubentry = prefs.getBoolean(getString(R.string.saveOnlyFirstSubentryKey), false);\r
221     if (prefs.getBoolean(getString(R.string.vibrateOnFailedSearchKey), true)) {\r
222       vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);\r
223     }\r
224     Log.d(LOG, "wordList=" + wordList + ", saveOnlyFirstSubentry=" + saveOnlyFirstSubentry);\r
225   }\r
226   \r
227   \r
228   @Override\r
229   protected void onResume() {\r
230     super.onResume();\r
231   }\r
232 \r
233   @Override\r
234   protected void onPause() {\r
235     super.onPause();\r
236   }\r
237 \r
238   @Override\r
239   protected void onDestroy() {\r
240     super.onDestroy();\r
241     setDictionaryPrefs(this, dictIndex, indexIndex, searchText.getText().toString());\r
242     try {\r
243       dictRaf.close();\r
244     } catch (IOException e) {\r
245       Log.e(LOG, "Failed to close dictionary", e);\r
246     }\r
247   }\r
248 \r
249   // --------------------------------------------------------------------------\r
250   // Buttons\r
251   // --------------------------------------------------------------------------\r
252 \r
253   private void onClearSearchTextButton(final Button clearSearchTextButton) {\r
254     clearSearchTextButton.requestFocus();\r
255     searchText.setText("");\r
256     searchText.requestFocus();\r
257     Log.d(LOG, "Trying to show soft keyboard.");\r
258     final InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);\r
259     manager.showSoftInput(searchText, InputMethodManager.SHOW_IMPLICIT);\r
260   }\r
261   \r
262   void updateLangButton() {\r
263     langButton.setText(index.shortName.toUpperCase());\r
264   }\r
265 \r
266   void onLanguageButton() {\r
267     if (currentSearchOperation != null) {\r
268       currentSearchOperation.interrupted.set(true);\r
269       currentSearchOperation = null;\r
270     }\r
271     \r
272     indexIndex = (indexIndex + 1) % dictionary.indices.size();\r
273     index = dictionary.indices.get(indexIndex);\r
274     indexAdapter = new IndexAdapter(index);\r
275     Log.d(LOG, "onLanguageButton, newLang=" + index.longName);\r
276     setListAdapter(indexAdapter);\r
277     updateLangButton();\r
278     onSearchTextChange(searchText.getText().toString());\r
279   }\r
280   \r
281   void onUpDownButton(final boolean up) {\r
282     final int firstVisibleRow = getListView().getFirstVisiblePosition();\r
283     final RowBase row = index.rows.get(firstVisibleRow);\r
284     final TokenRow tokenRow = row.getTokenRow(true);\r
285     final int destIndexEntry;\r
286     if (up) {\r
287       if (row != tokenRow) {\r
288         destIndexEntry = tokenRow.referenceIndex;\r
289       } else {\r
290         destIndexEntry = Math.max(tokenRow.referenceIndex - 1, 0);\r
291       }\r
292     } else {\r
293       // Down\r
294       destIndexEntry = Math.min(tokenRow.referenceIndex + 1, index.sortedIndexEntries.size());\r
295     }\r
296     \r
297     final Index.IndexEntry dest = index.sortedIndexEntries.get(destIndexEntry);\r
298     searchText.setText(dest.token);\r
299     Log.d(LOG, "onUpDownButton, destIndexEntry=" + dest.token);\r
300     //jumpToRow(index.sortedIndexEntries.get(destIndexEntry).startRow);\r
301   }\r
302 \r
303   // --------------------------------------------------------------------------\r
304   // Options Menu\r
305   // --------------------------------------------------------------------------\r
306   \r
307   @Override\r
308   public boolean onCreateOptionsMenu(final Menu menu) {\r
309     \r
310     {\r
311     final MenuItem preferences = menu.add(getString(R.string.preferences));\r
312     preferences.setOnMenuItemClickListener(new OnMenuItemClickListener() {\r
313       public boolean onMenuItemClick(final MenuItem menuItem) {\r
314         startActivity(new Intent(DictionaryActivity.this,\r
315             PreferenceActivity.class));\r
316         return false;\r
317       }\r
318     });\r
319     }\r
320 \r
321     {\r
322     final MenuItem dictionaryList = menu.add(getString(R.string.dictionaryList));\r
323     dictionaryList.setOnMenuItemClickListener(new OnMenuItemClickListener() {\r
324       public boolean onMenuItemClick(final MenuItem menuItem) {\r
325         startActivity(DictionaryListActivity.getIntent(DictionaryActivity.this));\r
326         startActivity(DictionaryListActivity.getIntent(DictionaryActivity.this));\r
327         return false;\r
328       }\r
329     });\r
330     }\r
331 \r
332     {\r
333       final MenuItem dictionaryList = menu.add(getString(R.string.editDictionary));\r
334       dictionaryList.setOnMenuItemClickListener(new OnMenuItemClickListener() {\r
335         public boolean onMenuItemClick(final MenuItem menuItem) {\r
336           final Intent intent = DictionaryEditActivity.getIntent(dictIndex);\r
337           startActivity(intent);\r
338           return false;\r
339         }\r
340       });\r
341       }\r
342 \r
343     return true;\r
344   }\r
345 \r
346 \r
347   // --------------------------------------------------------------------------\r
348   // Context Menu + clicks\r
349   // --------------------------------------------------------------------------\r
350 \r
351   @Override\r
352   public void onCreateContextMenu(ContextMenu menu, View v,\r
353       ContextMenuInfo menuInfo) {\r
354     AdapterContextMenuInfo adapterContextMenuInfo = (AdapterContextMenuInfo) menuInfo;\r
355     final RowBase row = (RowBase) getListAdapter().getItem(adapterContextMenuInfo.position);\r
356 \r
357     final MenuItem addToWordlist = menu.add(getString(R.string.addToWordList, wordList.getName()));\r
358     addToWordlist.setOnMenuItemClickListener(new OnMenuItemClickListener() {\r
359       public boolean onMenuItemClick(MenuItem item) {\r
360         onAppendToWordList(row);\r
361         return false;\r
362       }\r
363     });\r
364 \r
365     final MenuItem copy = menu.add(android.R.string.copy);\r
366     copy.setOnMenuItemClickListener(new OnMenuItemClickListener() {\r
367       public boolean onMenuItemClick(MenuItem item) {\r
368         onCopy(row);\r
369         return false;\r
370       }\r
371     });\r
372 \r
373   }\r
374   \r
375   @Override\r
376   protected void onListItemClick(ListView l, View v, int row, long id) {\r
377     openContextMenu(v);\r
378   }\r
379   \r
380   void onAppendToWordList(final RowBase row) {\r
381     final StringBuilder rawText = new StringBuilder();\r
382     rawText.append(\r
383         new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").format(new Date()))\r
384         .append("\t");\r
385     rawText.append(index.longName).append("\t");\r
386     rawText.append(row.getTokenRow(true).getToken()).append("\t");\r
387     rawText.append(row.getRawText(saveOnlyFirstSubentry));\r
388     Log.d(LOG, "Writing : " + rawText);\r
389     try {\r
390       wordList.getParentFile().mkdirs();\r
391       final PrintWriter out = new PrintWriter(\r
392           new FileWriter(wordList, true));\r
393       out.println(rawText.toString());\r
394       out.close();\r
395     } catch (IOException e) {\r
396       Log.e(LOG, "Unable to append to " + wordList.getAbsolutePath(), e);\r
397       Toast.makeText(this, getString(R.string.failedAddingToWordList, wordList.getAbsolutePath()), Toast.LENGTH_LONG);\r
398     }\r
399     return;\r
400   }\r
401 \r
402   void onCopy(final RowBase row) {\r
403     Log.d(LOG, "Copy, row=" + row);\r
404     final StringBuilder result = new StringBuilder();\r
405     result.append(row.getRawText(false));\r
406     final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);\r
407     clipboardManager.setText(result.toString());\r
408     Log.d(LOG, "Copied: " + result);\r
409   }\r
410 \r
411   @Override\r
412   public boolean onKeyDown(final int keyCode, final KeyEvent event) {\r
413     if (event.getUnicodeChar() != 0) {\r
414       if (!searchText.hasFocus()) {\r
415         searchText.setText("" + (char) event.getUnicodeChar());\r
416         onSearchTextChange(searchText.getText().toString());\r
417         searchText.requestFocus();\r
418       }\r
419       return true;\r
420     }\r
421     return super.onKeyDown(keyCode, event);\r
422   }\r
423 \r
424 \r
425   // --------------------------------------------------------------------------\r
426   // SearchOperation\r
427   // --------------------------------------------------------------------------\r
428 \r
429   private void searchFinished(final SearchOperation searchOperation) {\r
430     if (searchOperation.interrupted.get()) {\r
431       Log.d(LOG, "Search operation was interrupted: " + searchOperation);\r
432       return;\r
433     }\r
434     if (searchOperation != this.currentSearchOperation) {\r
435       Log.d(LOG, "Stale searchOperation finished: " + searchOperation);\r
436       return;\r
437     }\r
438     \r
439     final Index.IndexEntry searchResult = searchOperation.searchResult;\r
440     Log.d(LOG, "searchFinished: " + searchOperation + ", searchResult=" + searchResult);\r
441 \r
442     jumpToRow(searchResult.longestPrefix.startRow);\r
443     \r
444 //    if (!searchResult.success) {\r
445 //      if (vibrator != null) {\r
446 //        vibrator.vibrate(VIBRATE_MILLIS);\r
447 //      }\r
448 //      searchText.setText(searchResult.longestPrefixString);\r
449 //      searchText.setSelection(searchResult.longestPrefixString.length());\r
450 //      return;\r
451 //    }\r
452   }\r
453   \r
454   private final void jumpToRow(final int row) {\r
455     setSelection(row);\r
456     getListView().setSelected(true);\r
457   }\r
458 \r
459   final class SearchOperation implements Runnable {\r
460     \r
461     final AtomicBoolean interrupted = new AtomicBoolean(false);\r
462     final String searchText;\r
463     final Index index;\r
464     \r
465     long searchStartMillis;\r
466 \r
467     Index.IndexEntry searchResult;\r
468     \r
469     SearchOperation(final String searchText, final Index index) {\r
470       this.searchText = searchText.trim();\r
471       this.index = index;\r
472     }\r
473     \r
474     public String toString() {\r
475       return String.format("SearchOperation(%s,%s)", searchText, interrupted.toString());\r
476     }\r
477 \r
478     @Override\r
479     public void run() {\r
480       searchStartMillis = System.currentTimeMillis();\r
481       searchResult = index.findInsertionPoint(searchText, interrupted);\r
482       Log.d(LOG, "searchText=" + searchText + ", searchDuration="\r
483           + (System.currentTimeMillis() - searchStartMillis) + ", interrupted="\r
484           + interrupted.get());\r
485       if (!interrupted.get()) {\r
486         uiHandler.post(new Runnable() {\r
487           @Override\r
488           public void run() {            \r
489             searchFinished(SearchOperation.this);\r
490           }\r
491         });\r
492       }\r
493     }\r
494   }\r
495 \r
496   \r
497   // --------------------------------------------------------------------------\r
498   // IndexAdapter\r
499   // --------------------------------------------------------------------------\r
500 \r
501   static final class IndexAdapter extends BaseAdapter {\r
502     \r
503     final Index index;\r
504 \r
505     IndexAdapter(final Index index) {\r
506       this.index = index;\r
507     }\r
508 \r
509     @Override\r
510     public int getCount() {\r
511       return index.rows.size();\r
512     }\r
513 \r
514     @Override\r
515     public RowBase getItem(int position) {\r
516       return index.rows.get(position);\r
517     }\r
518 \r
519     @Override\r
520     public long getItemId(int position) {\r
521       return getItem(position).index();\r
522     }\r
523 \r
524     @Override\r
525     public View getView(int position, View convertView, ViewGroup parent) {\r
526       final RowBase row = index.rows.get(position);\r
527       if (row instanceof PairEntry.Row) {\r
528         return getView((PairEntry.Row) row, parent);\r
529       } else if (row instanceof TokenRow) {\r
530         return getView((TokenRow) row, parent);\r
531       } else {\r
532         throw new IllegalArgumentException("Unsupported Row type: " + row.getClass());\r
533       }\r
534     }\r
535 \r
536     private View getView(PairEntry.Row row, ViewGroup parent) {\r
537       final TableLayout result = new TableLayout(parent.getContext());\r
538       final PairEntry entry = row.getEntry();\r
539       final int rowCount = entry.pairs.length;\r
540       for (int r = 0; r < rowCount; ++r) {\r
541         final TableRow tableRow = new TableRow(result.getContext());\r
542 \r
543         TextView column1 = new TextView(tableRow.getContext());\r
544         TextView column2 = new TextView(tableRow.getContext());\r
545         final TableRow.LayoutParams layoutParams = new TableRow.LayoutParams();\r
546         layoutParams.weight = 0.5f;\r
547 \r
548         if (r > 0) {\r
549           final TextView spacer = new TextView(tableRow.getContext());\r
550           spacer.setText(" • ");\r
551           tableRow.addView(spacer);\r
552         }\r
553         tableRow.addView(column1, layoutParams);\r
554         if (r > 0) {\r
555           final TextView spacer = new TextView(tableRow.getContext());\r
556           spacer.setText(" • ");\r
557           tableRow.addView(spacer);\r
558         }\r
559         tableRow.addView(column2, layoutParams);\r
560 \r
561         column1.setWidth(1);\r
562         column2.setWidth(1);\r
563 \r
564         // TODO: color words by gender\r
565         final String col1Text = index.swapPairEntries ? entry.pairs[r].lang2 : entry.pairs[r].lang1;\r
566         column1.setText(col1Text, TextView.BufferType.SPANNABLE);\r
567         final Spannable col1Spannable = (Spannable) column1.getText();\r
568         \r
569         int startPos = 0;\r
570         final String token = row.getTokenRow(true).getToken();\r
571         while ((startPos = col1Text.indexOf(token, startPos)) != -1) {\r
572           col1Spannable.setSpan(new StyleSpan(Typeface.BOLD), startPos,\r
573               startPos + token.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);\r
574           startPos += token.length();\r
575         }\r
576 \r
577         final String col2Text = index.swapPairEntries ? entry.pairs[r].lang1 : entry.pairs[r].lang2;\r
578         column2.setText(col2Text, TextView.BufferType.NORMAL);\r
579 \r
580         result.addView(tableRow);\r
581       }\r
582 \r
583       return result;\r
584     }\r
585 \r
586     private View getView(TokenRow row, ViewGroup parent) {\r
587       final TextView textView = new TextView(parent.getContext());\r
588       textView.setText(row.getToken());\r
589       textView.setTextSize(20);\r
590       return textView;\r
591     }\r
592     \r
593   }\r
594 \r
595   // --------------------------------------------------------------------------\r
596   // SearchText\r
597   // --------------------------------------------------------------------------\r
598 \r
599   void onSearchTextChange(final String text) {\r
600     if (!searchText.isFocused()) {\r
601       return;\r
602     }\r
603     Log.d(LOG, "onSearchTextChange: " + text);    \r
604     if (currentSearchOperation != null) {\r
605       Log.d(LOG, "Interrupting currentSearchOperation.");\r
606       currentSearchOperation.interrupted.set(true);\r
607     }\r
608     currentSearchOperation = new SearchOperation(text, index);\r
609     searchExecutor.execute(currentSearchOperation);\r
610   }\r
611   \r
612   private class SearchTextWatcher implements TextWatcher {\r
613     public void afterTextChanged(final Editable searchTextEditable) {\r
614       if (searchText.hasFocus()) {\r
615         Log.d(LOG, "Search text changed with focus: " + searchText.getText());\r
616         // If they were typing to cause the change, update the UI.\r
617         onSearchTextChange(searchText.getText().toString());\r
618       }\r
619     }\r
620 \r
621     public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,\r
622         int arg3) {\r
623     }\r
624 \r
625     public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {\r
626     }\r
627   }\r
628 \r
629 }\r