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