X-Git-Url: http://gitweb.fperrin.net/?a=blobdiff_plain;f=src%2Fcom%2Fhughes%2Fandroid%2Fdictionary%2FDictionaryActivity.java;h=356e957ebaf8788645e898a1ac3d6b801ba04e7b;hb=7d31142fbfb0ace1d668e9b8b6e723a1dc4ffe0b;hp=cdfe6f979a6d182aae1a34bd563c11af48d8cb19;hpb=d17d0530baa6011c3cbfad6607c978af23790e05;p=Dictionary.git diff --git a/src/com/hughes/android/dictionary/DictionaryActivity.java b/src/com/hughes/android/dictionary/DictionaryActivity.java index cdfe6f9..356e957 100644 --- a/src/com/hughes/android/dictionary/DictionaryActivity.java +++ b/src/com/hughes/android/dictionary/DictionaryActivity.java @@ -1,5 +1,5 @@ // Copyright 2011 Google Inc. All Rights Reserved. -// +// Some Parts Copyright 2013 Dominik Köppl // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -14,27 +14,37 @@ package com.hughes.android.dictionary; +import android.annotation.SuppressLint; import android.app.Dialog; -import android.app.ListActivity; +import android.app.SearchManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.graphics.Color; import android.graphics.Typeface; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; +import android.speech.tts.TextToSpeech; +import android.speech.tts.TextToSpeech.OnInitListener; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.SearchView; +import android.support.v7.widget.SearchView.OnQueryTextListener; +import android.support.v7.widget.Toolbar; import android.text.ClipboardManager; -import android.text.Editable; -import android.text.Selection; +import android.text.InputType; import android.text.Spannable; -import android.text.TextWatcher; import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; import android.text.style.StyleSpan; import android.util.Log; import android.util.TypedValue; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; +import android.view.Gravity; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; @@ -45,20 +55,24 @@ import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.Button; -import android.widget.EditText; +import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; +import android.widget.ImageView.ScaleType; import android.widget.LinearLayout; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; +import android.widget.TextView.BufferType; import android.widget.Toast; import com.hughes.android.dictionary.DictionaryInfo.IndexInfo; @@ -67,6 +81,7 @@ import com.hughes.android.dictionary.engine.EntrySource; import com.hughes.android.dictionary.engine.HtmlEntry; import com.hughes.android.dictionary.engine.Index; import com.hughes.android.dictionary.engine.Index.IndexEntry; +import com.hughes.android.dictionary.engine.Language.LanguageResources; import com.hughes.android.dictionary.engine.PairEntry; import com.hughes.android.dictionary.engine.PairEntry.Pair; import com.hughes.android.dictionary.engine.RowBase; @@ -74,6 +89,7 @@ import com.hughes.android.dictionary.engine.TokenRow; import com.hughes.android.dictionary.engine.TransliteratorManager; import com.hughes.android.util.IntentLauncher; import com.hughes.android.util.NonLinkClickableSpan; +import com.hughes.util.StringUtil; import java.io.File; import java.io.FileWriter; @@ -84,8 +100,10 @@ import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import java.util.Random; import java.util.Set; import java.util.concurrent.Executor; @@ -95,16 +113,13 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class DictionaryActivity extends ListActivity { +public class DictionaryActivity extends ActionBarActivity { static final String LOG = "QuickDic"; - private String initialSearchText; - DictionaryApplication application; File dictFile = null; - RandomAccessFile dictRaf = null; Dictionary dictionary = null; @@ -115,7 +130,8 @@ public class DictionaryActivity extends ListActivity { List rowsToShow = null; // if not null, just show these rows. - // package for test. + final Random rand = new Random(); + final Handler uiHandler = new Handler(); private final Executor searchExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() { @@ -127,28 +143,44 @@ public class DictionaryActivity extends ListActivity { private SearchOperation currentSearchOperation = null; - C.Theme theme = C.Theme.LIGHT; + TextToSpeech textToSpeech; + volatile boolean ttsReady; Typeface typeface; - + DictionaryApplication.Theme theme = DictionaryApplication.Theme.LIGHT; + int textColorFg = Color.BLACK; int fontSizeSp; - EditText searchText; + private ListView listView; + private ListView getListView() { + if (listView == null) { + listView = (ListView)findViewById(android.R.id.list); + } + return listView; + } + + private void setListAdapter(ListAdapter adapter) { + getListView().setAdapter(adapter); + } + + private ListAdapter getListAdapter() { + return getListView().getAdapter(); + } + + SearchView searchView; + ImageButton languageButton; + SearchView.OnQueryTextListener onQueryTextListener; - Button langButton; + MenuItem nextWordMenuItem, previousWordMenuItem, randomWordMenuItem; // Never null. private File wordList = null; - private boolean saveOnlyFirstSubentry = false; - private boolean clickOpensContextMenu = false; // Visible for testing. ListAdapter indexAdapter = null; - final SearchTextWatcher searchTextWatcher = new SearchTextWatcher(); - /** * For some languages, loading the transliterators used in this search takes * a long time, so we fire it up on a different thread, and don't invoke it @@ -159,13 +191,11 @@ public class DictionaryActivity extends ListActivity { public DictionaryActivity() { } - public static Intent getLaunchIntent(final File dictFile, final int indexIndex, + public static Intent getLaunchIntent(Context c, final File dictFile, final String indexShortName, final String searchToken) { - final Intent intent = new Intent(); - intent.setClassName(DictionaryActivity.class.getPackage().getName(), - DictionaryActivity.class.getName()); + final Intent intent = new Intent(c, DictionaryActivity.class); intent.putExtra(C.DICT_FILE, dictFile.getPath()); - intent.putExtra(C.INDEX_INDEX, indexIndex); + intent.putExtra(C.INDEX_SHORT_NAME, indexShortName); intent.putExtra(C.SEARCH_TOKEN, searchToken); return intent; } @@ -173,34 +203,135 @@ public class DictionaryActivity extends ListActivity { @Override protected void onSaveInstanceState(final Bundle outState) { super.onSaveInstanceState(outState); - Log.d(LOG, "onSaveInstanceState: " + searchText.getText().toString()); - outState.putInt(C.INDEX_INDEX, indexIndex); - outState.putString(C.SEARCH_TOKEN, searchText.getText().toString()); - } - - @Override - protected void onRestoreInstanceState(final Bundle outState) { - super.onRestoreInstanceState(outState); - Log.d(LOG, "onRestoreInstanceState: " + outState.getString(C.SEARCH_TOKEN)); - onCreate(outState); + Log.d(LOG, "onSaveInstanceState: " + searchView.getQuery().toString()); + outState.putString(C.INDEX_SHORT_NAME, index.shortName); + outState.putString(C.SEARCH_TOKEN, searchView.getQuery().toString()); } @Override public void onCreate(Bundle savedInstanceState) { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - prefs.edit().remove(C.INDEX_INDEX).commit(); // Don't auto-launch if - // this fails. - + // This needs to be before super.onCreate, otherwise ActionbarSherlock + // doesn't makes the background of the actionbar white when you're + // in the dark theme. setTheme(((DictionaryApplication) getApplication()).getSelectedTheme().themeId); Log.d(LOG, "onCreate:" + this); super.onCreate(savedInstanceState); + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + + // Don't auto-launch if this fails. + prefs.edit().remove(C.DICT_FILE).commit(); + + setContentView(R.layout.dictionary_activity); + application = (DictionaryApplication) getApplication(); theme = application.getSelectedTheme(); + textColorFg = getResources().getColor(theme.tokenRowFgColor); final Intent intent = getIntent(); - dictFile = new File(intent.getStringExtra(C.DICT_FILE)); + String intentAction = intent.getAction(); + /** + * @author Dominik Köppl Querying the Intent + * com.hughes.action.ACTION_SEARCH_DICT is the advanced query + * Arguments: SearchManager.QUERY -> the phrase to search from + * -> language in which the phrase is written to -> to which + * language shall be translated + */ + if (intentAction != null && intentAction.equals("com.hughes.action.ACTION_SEARCH_DICT")) + { + String query = intent.getStringExtra(SearchManager.QUERY); + String from = intent.getStringExtra("from"); + if (from != null) + from = from.toLowerCase(Locale.US); + String to = intent.getStringExtra("to"); + if (to != null) + to = to.toLowerCase(Locale.US); + if (query != null) + { + getIntent().putExtra(C.SEARCH_TOKEN, query); + } + if (intent.getStringExtra(C.DICT_FILE) == null && (from != null || to != null)) + { + Log.d(LOG, "DictSearch: from: " + from + " to " + to); + List dicts = application.getDictionariesOnDevice(null); + for (DictionaryInfo info : dicts) + { + boolean hasFrom = from == null; + boolean hasTo = to == null; + for (IndexInfo index : info.indexInfos) + { + if (!hasFrom && index.shortName.toLowerCase(Locale.US).equals(from)) + hasFrom = true; + if (!hasTo && index.shortName.toLowerCase(Locale.US).equals(to)) + hasTo = true; + } + if (hasFrom && hasTo) + { + if (from != null) + { + int which_index = 0; + for (; which_index < info.indexInfos.size(); ++which_index) + { + if (info.indexInfos.get(which_index).shortName.toLowerCase( + Locale.US).equals(from)) + break; + } + intent.putExtra(C.INDEX_SHORT_NAME, + info.indexInfos.get(which_index).shortName); + + } + intent.putExtra(C.DICT_FILE, application.getPath(info.uncompressedFilename) + .toString()); + break; + } + } + + } + } + /** + * @author Dominik Köppl Querying the Intent Intent.ACTION_SEARCH is a + * simple query Arguments follow from android standard (see + * documentation) + */ + if (intentAction != null && intentAction.equals(Intent.ACTION_SEARCH)) + { + String query = intent.getStringExtra(SearchManager.QUERY); + if (query != null) + getIntent().putExtra(C.SEARCH_TOKEN, query); + } + /** + * @author Dominik Köppl If no dictionary is chosen, use the default + * dictionary specified in the preferences If this step does + * fail (no default directory specified), show a toast and + * abort. + */ + if (intent.getStringExtra(C.DICT_FILE) == null) + { + String dictfile = prefs.getString(getString(R.string.defaultDicKey), null); + if (dictfile != null) + intent.putExtra(C.DICT_FILE, application.getPath(dictfile).toString()); + } + String dictFilename = intent.getStringExtra(C.DICT_FILE); + + if (dictFilename == null) + { + Toast.makeText(this, getString(R.string.no_dict_file), Toast.LENGTH_LONG).show(); + startActivity(DictionaryManagerActivity.getLaunchIntent(getApplicationContext())); + finish(); + return; + } + if (dictFilename != null) + dictFile = new File(dictFilename); + + ttsReady = false; + textToSpeech = new TextToSpeech(getApplicationContext(), new OnInitListener() { + @Override + public void onInit(int status) { + ttsReady = true; + updateTTSLanguage(indexIndex); + } + }); try { final String name = application.getDictionaryName(dictFile.getName()); @@ -219,22 +350,30 @@ public class DictionaryActivity extends ListActivity { } Toast.makeText(this, getString(R.string.invalidDictionary, "", e.getMessage()), Toast.LENGTH_LONG).show(); - startActivity(DictionaryManagerActivity.getLaunchIntent()); + startActivity(DictionaryManagerActivity.getLaunchIntent(getApplicationContext())); finish(); return; } - indexIndex = intent.getIntExtra(C.INDEX_INDEX, 0); - if (savedInstanceState != null) { - indexIndex = savedInstanceState.getInt(C.INDEX_INDEX, indexIndex); + String targetIndex = intent.getStringExtra(C.INDEX_SHORT_NAME); + if (savedInstanceState != null && savedInstanceState.getString(C.INDEX_SHORT_NAME) != null) { + targetIndex = savedInstanceState.getString(C.INDEX_SHORT_NAME); + } + indexIndex = 0; + for (int i = 0; i < dictionary.indices.size(); ++i) { + if (dictionary.indices.get(i).shortName.equals(targetIndex)) { + indexIndex = i; + break; + } } - indexIndex %= dictionary.indices.size(); Log.d(LOG, "Loading index " + indexIndex); index = dictionary.indices.get(indexIndex); + getListView().setEmptyView(findViewById(android.R.id.empty)); setListAdapter(new IndexAdapter(index)); // Pre-load the collators. new Thread(new Runnable() { public void run() { + android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND); final long startMillis = System.currentTimeMillis(); try { TransliteratorManager.init(new TransliteratorManager.Callback() { @@ -243,7 +382,7 @@ public class DictionaryActivity extends ListActivity { uiHandler.post(new Runnable() { @Override public void run() { - onSearchTextChange(searchText.getText().toString()); + onSearchTextChange(searchView.getQuery().toString()); } }); } @@ -252,8 +391,8 @@ public class DictionaryActivity extends ListActivity { for (final Index index : dictionary.indices) { final String searchToken = index.sortedIndexEntries.get(0).token; final IndexEntry entry = index.findExact(searchToken); - if (!searchToken.equals(entry.token)) { - Log.e(LOG, "Couldn't find token: " + searchToken + ", " + entry.token); + if (entry == null || !searchToken.equals(entry.token)) { + Log.e(LOG, "Couldn't find token: " + searchToken + ", " + (entry == null ? "null" : entry.token)); } } indexPrepFinished = true; @@ -265,10 +404,19 @@ public class DictionaryActivity extends ListActivity { } }).start(); - String fontName = prefs.getString(getString(R.string.fontKey), "FreeSerif.ttf.jpg"); + String fontName = prefs.getString(getString(R.string.fontKey), "FreeSerif.otf.jpg"); if ("SYSTEM".equals(fontName)) { typeface = Typeface.DEFAULT; + } else if ("SERIF".equals(fontName)) { + typeface = Typeface.SERIF; + } else if ("SANS_SERIF".equals(fontName)) { + typeface = Typeface.SANS_SERIF; + } else if ("MONOSPACE".equals(fontName)) { + typeface = Typeface.MONOSPACE; } else { + if ("FreeSerif.ttf.jpg".equals(fontName)) { + fontName = "FreeSerif.otf.jpg"; + } try { typeface = Typeface.createFromAsset(getAssets(), fontName); } catch (Exception e) { @@ -277,10 +425,6 @@ public class DictionaryActivity extends ListActivity { Toast.LENGTH_LONG).show(); } } - // if (!"SYSTEM".equals(fontName)) { - // throw new RuntimeException("Test force using system font: " + - // fontName); - // } if (typeface == null) { Log.w(LOG, "Unable to create typeface, using default."); typeface = Typeface.DEFAULT; @@ -292,10 +436,33 @@ public class DictionaryActivity extends ListActivity { fontSizeSp = 14; } - setContentView(R.layout.dictionary_activity); - searchText = (EditText) findViewById(R.id.SearchText); - searchText.requestFocus(); - searchText.addTextChangedListener(searchTextWatcher); + // ContextMenu. + registerForContextMenu(getListView()); + + // Cache some prefs. + wordList = application.getWordListFile(); + saveOnlyFirstSubentry = prefs.getBoolean(getString(R.string.saveOnlyFirstSubentryKey), + false); + clickOpensContextMenu = prefs.getBoolean(getString(R.string.clickOpensContextMenuKey), + false); + Log.d(LOG, "wordList=" + wordList + ", saveOnlyFirstSubentry=" + saveOnlyFirstSubentry); + + onCreateSetupActionBarAndSearchView(); + + View floatSwapButton = findViewById(R.id.floatSwapButton); + floatSwapButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + onLanguageButtonClick(); + } + }); + floatSwapButton.setOnLongClickListener(new OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + onLanguageButtonLongClick(v.getContext()); + return true; + } + }); // Set the search text from the intent, then the saved state. String text = getIntent().getStringExtra(C.SEARCH_TOKEN); @@ -308,93 +475,108 @@ public class DictionaryActivity extends ListActivity { setSearchText(text, true); Log.d(LOG, "Trying to restore searchText=" + text); - final View clearSearchTextButton = findViewById(R.id.ClearSearchTextButton); - clearSearchTextButton.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - onClearSearchTextButton(); + setDictionaryPrefs(this, dictFile, index.shortName, searchView.getQuery().toString()); + + updateLangButton(); + searchView.requestFocus(); + + // http://stackoverflow.com/questions/2833057/background-listview-becomes-black-when-scrolling +// getListView().setCacheColorHint(0); + } + + private void onCreateSetupActionBarAndSearchView() { + ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayShowTitleEnabled(false); + actionBar.setDisplayShowHomeEnabled(false); + actionBar.setDisplayHomeAsUpEnabled(false); + + final LinearLayout customSearchView = new LinearLayout(getSupportActionBar().getThemedContext()); + + final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + customSearchView.setLayoutParams(layoutParams); + + listView.setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int row, long id) { + onListItemClick(getListView(), view, row, id); } }); - clearSearchTextButton.setVisibility(PreferenceManager.getDefaultSharedPreferences(this) - .getBoolean(getString(R.string.showClearSearchTextButtonKey), true) ? View.VISIBLE - : View.GONE); - - langButton = (Button) findViewById(R.id.LangButton); - langButton.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - onLanguageButton(); + + languageButton = new ImageButton(customSearchView.getContext()); + languageButton.setScaleType(ScaleType.FIT_CENTER); + languageButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + onLanguageButtonClick(); } }); - langButton.setOnLongClickListener(new OnLongClickListener() { + languageButton.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { onLanguageButtonLongClick(v.getContext()); return true; } }); - updateLangButton(); - - final View upButton = findViewById(R.id.UpButton); - upButton.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - onUpDownButton(true); - } - }); - final View downButton = findViewById(R.id.DownButton); - downButton.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - onUpDownButton(false); - } - }); - - getListView().setOnItemSelectedListener(new ListView.OnItemSelectedListener() { + languageButton.setAdjustViewBounds(true); + LinearLayout.LayoutParams lpb = new LinearLayout.LayoutParams(application.languageButtonPixels, LinearLayout.LayoutParams.MATCH_PARENT); + customSearchView.addView(languageButton, lpb); + + searchView = new SearchView(getSupportActionBar().getThemedContext()); + + // Get rid of search icon, it takes up too much space. + // There is still text saying "search" in the search field. + searchView.setIconifiedByDefault(true); + searchView.setIconified(false); + + searchView.setQueryHint(getString(R.string.searchText)); + searchView.setSubmitButtonEnabled(false); + searchView.setInputType(InputType.TYPE_CLASS_TEXT); + searchView.setImeOptions( + EditorInfo.IME_ACTION_DONE | + EditorInfo.IME_FLAG_NO_EXTRACT_UI | + // EditorInfo.IME_FLAG_NO_FULLSCREEN | // Requires API + // 11 + EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + onQueryTextListener = new OnQueryTextListener() { @Override - public void onItemSelected(AdapterView adapterView, View arg1, final int position, - long id) { - if (!searchText.isFocused()) { - if (!isFiltered()) { - final RowBase row = (RowBase) getListAdapter().getItem(position); - Log.d(LOG, "onItemSelected: " + row.index()); - final TokenRow tokenRow = row.getTokenRow(true); - searchText.setText(tokenRow.getToken()); - } - } + public boolean onQueryTextSubmit(String query) { + Log.d(LOG, "OnQueryTextListener: onQueryTextSubmit: " + searchView.getQuery()); + hideKeyboard(); + return true; } @Override - public void onNothingSelected(AdapterView arg0) { + public boolean onQueryTextChange(String newText) { + Log.d(LOG, "OnQueryTextListener: onQueryTextChange: " + searchView.getQuery()); + onSearchTextChange(searchView.getQuery().toString()); + return true; } - }); - - // ContextMenu. - registerForContextMenu(getListView()); - - // Prefs. - wordList = new File(prefs.getString(getString(R.string.wordListFileKey), - getString(R.string.wordListFileDefault))); - saveOnlyFirstSubentry = prefs.getBoolean(getString(R.string.saveOnlyFirstSubentryKey), - false); - clickOpensContextMenu = prefs.getBoolean(getString(R.string.clickOpensContextMenuKey), - false); - // if (prefs.getBoolean(getString(R.string.vibrateOnFailedSearchKey), - // true)) { - // vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); - // } - Log.d(LOG, "wordList=" + wordList + ", saveOnlyFirstSubentry=" + saveOnlyFirstSubentry); - - setDictionaryPrefs(this, dictFile, indexIndex, searchText.getText().toString()); + }; + searchView.setOnQueryTextListener(onQueryTextListener); + searchView.setFocusable(true); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0, + FrameLayout.LayoutParams.WRAP_CONTENT, 1); + customSearchView.addView(searchView, lp); + + actionBar.setCustomView(customSearchView); + actionBar.setDisplayShowCustomEnabled(true); + + // Avoid wasting space on large left inset + Toolbar tb = (Toolbar)customSearchView.getParent(); + tb.setContentInsetsRelative(0, 0); } @Override protected void onResume() { + Log.d(LOG, "onResume"); super.onResume(); if (PreferenceActivity.prefsMightHaveChanged) { PreferenceActivity.prefsMightHaveChanged = false; finish(); startActivity(getIntent()); } - if (initialSearchText != null) { - setSearchText(initialSearchText, true); - } + showKeyboard(); } @Override @@ -402,12 +584,25 @@ public class DictionaryActivity extends ListActivity { super.onPause(); } + @Override + /** + * Invoked when MyWebView returns, since the user might have clicked some + * hypertext in the MyWebView. + */ + protected void onActivityResult(int requestCode, int resultCode, Intent result) { + super.onActivityResult(requestCode, resultCode, result); + if (result != null && result.hasExtra(C.SEARCH_TOKEN)) { + Log.d(LOG, "onActivityResult: " + result.getStringExtra(C.SEARCH_TOKEN)); + jumpToTextFromHyperLink(result.getStringExtra(C.SEARCH_TOKEN), indexIndex); + } + } + private static void setDictionaryPrefs(final Context context, final File dictFile, - final int indexIndex, final String searchToken) { + final String indexShortName, final String searchToken) { final SharedPreferences.Editor prefs = PreferenceManager.getDefaultSharedPreferences( context).edit(); prefs.putString(C.DICT_FILE, dictFile.getPath()); - prefs.putInt(C.INDEX_INDEX, indexIndex); + prefs.putString(C.INDEX_SHORT_NAME, indexShortName); prefs.putString(C.SEARCH_TOKEN, ""); // Don't need to save search token. prefs.commit(); } @@ -442,31 +637,78 @@ public class DictionaryActivity extends ListActivity { // Buttons // -------------------------------------------------------------------------- - private void onClearSearchTextButton() { - setSearchText("", true); - Log.d(LOG, "Trying to show soft keyboard."); - final InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - manager.showSoftInput(searchText, InputMethodManager.SHOW_IMPLICIT); + private void showKeyboard() { + // For some reason, this doesn't always work the first time. + // One way to replicate the problem: + // Press the "task switch" button repeatedly to pause and resume + for (int delay = 1; delay <= 101; delay += 100) { + searchView.postDelayed(new Runnable() { + @Override + public void run() { + Log.d(LOG, "Trying to show soft keyboard."); + final boolean searchTextHadFocus = searchView.hasFocus(); + searchView.requestFocusFromTouch(); + final InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + manager.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT); + if (!searchTextHadFocus) { + defocusSearchText(); + } + } + }, delay); + } + } + + private void hideKeyboard() { + Log.d(LOG, "Hide soft keyboard."); + searchView.clearFocus(); + InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + manager.hideSoftInputFromWindow(searchView.getWindowToken(), 0); } void updateLangButton() { - // final LanguageResources languageResources = - // Language.isoCodeToResources.get(index.shortName); - // if (languageResources != null && languageResources.flagId != 0) { - // langButton.setCompoundDrawablesWithIntrinsicBounds(0, 0, - // languageResources.flagId, 0); - // } else { - // langButton.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); - langButton.setText(index.shortName); - // } + final LanguageResources languageResources = + DictionaryApplication.isoCodeToResources.get(index.shortName); + if (languageResources != null && languageResources.flagId != 0) { + languageButton.setImageResource(languageResources.flagId); + } else { + if (indexIndex % 2 == 0) { + languageButton.setImageResource(android.R.drawable.ic_media_next); + } else { + languageButton.setImageResource(android.R.drawable.ic_media_previous); + } + } + updateTTSLanguage(indexIndex); + } + + private void updateTTSLanguage(int i) { + if (!ttsReady || index == null || textToSpeech == null) { + Log.d(LOG, "Can't updateTTSLanguage."); + return; + } + final Locale locale = new Locale(dictionary.indices.get(i).sortLanguage.getIsoCode()); + Log.d(LOG, "Setting TTS locale to: " + locale); + try { + final int ttsResult = textToSpeech.setLanguage(locale); + if (ttsResult != TextToSpeech.LANG_AVAILABLE && + ttsResult != TextToSpeech.LANG_COUNTRY_AVAILABLE) { + Log.e(LOG, "TTS not available in this language: ttsResult=" + ttsResult); + } + } catch (Exception e) { + Toast.makeText(this, getString(R.string.TTSbroken), Toast.LENGTH_LONG).show(); + } } - void onLanguageButton() { + void onLanguageButtonClick() { + if (dictionary.indices.size() == 1) { + // No need to work to switch indices. + return; + } if (currentSearchOperation != null) { currentSearchOperation.interrupted.set(true); currentSearchOperation = null; } - changeIndexGetFocusAndResearch((indexIndex + 1) % dictionary.indices.size()); + setIndexAndSearchText((indexIndex + 1) % dictionary.indices.size(), + searchView.getQuery().toString(), false); } void onLanguageButtonLongClick(final Context context) { @@ -474,68 +716,63 @@ public class DictionaryActivity extends ListActivity { dialog.setContentView(R.layout.select_dictionary_dialog); dialog.setTitle(R.string.selectDictionary); - final List installedDicts = ((DictionaryApplication) getApplication()) - .getUsableDicts(); + final List installedDicts = application.getDictionariesOnDevice(null); ListView listView = (ListView) dialog.findViewById(android.R.id.list); - - // final LinearLayout.LayoutParams layoutParams = new - // LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, - // ViewGroup.LayoutParams.WRAP_CONTENT); - // layoutParams.width = 0; - // layoutParams.weight = 1.0f; - final Button button = new Button(listView.getContext()); final String name = getString(R.string.dictionaryManager); button.setText(name); final IntentLauncher intentLauncher = new IntentLauncher(listView.getContext(), - DictionaryManagerActivity.getLaunchIntent()) { + DictionaryManagerActivity.getLaunchIntent(getApplicationContext())) { @Override protected void onGo() { dialog.dismiss(); DictionaryActivity.this.finish(); - }; + } }; button.setOnClickListener(intentLauncher); - // button.setLayoutParams(layoutParams); listView.addHeaderView(button); - // listView.setHeaderDividersEnabled(true); listView.setAdapter(new BaseAdapter() { @Override public View getView(int position, View convertView, ViewGroup parent) { + final DictionaryInfo dictionaryInfo = getItem(position); + final LinearLayout result = new LinearLayout(parent.getContext()); - final DictionaryInfo dictionaryInfo = getItem(position); for (int i = 0; i < dictionaryInfo.indexInfos.size(); ++i) { final IndexInfo indexInfo = dictionaryInfo.indexInfos.get(i); - final Button button = new Button(parent.getContext()); -// final String name = application -// .getDictionaryName(dictionaryInfo.uncompressedFilename); - button.setText(indexInfo.shortName); + final View button = application.createButton(parent.getContext(), + dictionaryInfo, indexInfo); final IntentLauncher intentLauncher = new IntentLauncher(parent.getContext(), - getLaunchIntent(application.getPath(dictionaryInfo.uncompressedFilename), - i, searchText.getText().toString())) { + getLaunchIntent(getApplicationContext(), + application.getPath(dictionaryInfo.uncompressedFilename), + indexInfo.shortName, searchView.getQuery().toString())) { @Override protected void onGo() { dialog.dismiss(); DictionaryActivity.this.finish(); - }; + } }; button.setOnClickListener(intentLauncher); - final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - layoutParams.width = 0; - layoutParams.weight = 1.0f; - button.setLayoutParams(layoutParams); - result.addView(button); - - if (i > 0) { - final TextView dash = new TextView(parent.getContext()); - dash.setText("-"); - result.addView(dash); + if (i == indexIndex && dictFile != null && + dictFile.getName().equals(dictionaryInfo.uncompressedFilename)) { + button.setPressed(true); } + result.addView(button); } + + final TextView nameView = new TextView(parent.getContext()); + final String name = application + .getDictionaryName(dictionaryInfo.uncompressedFilename); + nameView.setText(name); + final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + layoutParams.width = 0; + layoutParams.weight = 1.0f; + nameView.setLayoutParams(layoutParams); + nameView.setGravity(Gravity.CENTER_VERTICAL); + result.addView(nameView); return result; } @@ -554,22 +791,9 @@ public class DictionaryActivity extends ListActivity { return installedDicts.size(); } }); - dialog.show(); } - private void changeIndexGetFocusAndResearch(final int newIndex) { - indexIndex = newIndex; - index = dictionary.indices.get(indexIndex); - indexAdapter = new IndexAdapter(index); - Log.d(LOG, "changingIndex, newLang=" + index.longName); - setListAdapter(indexAdapter); - updateLangButton(); - searchText.requestFocus(); // Otherwise, nothing may happen. - onSearchTextChange(searchText.getText().toString()); - setDictionaryPrefs(this, dictFile, indexIndex, searchText.getText().toString()); - } - void onUpDownButton(final boolean up) { if (isFiltered()) { return; @@ -586,18 +810,21 @@ public class DictionaryActivity extends ListActivity { } } else { // Down - destIndexEntry = Math.min(tokenRow.referenceIndex + 1, index.sortedIndexEntries.size()); + destIndexEntry = Math.min(tokenRow.referenceIndex + 1, index.sortedIndexEntries.size() - 1); } final Index.IndexEntry dest = index.sortedIndexEntries.get(destIndexEntry); Log.d(LOG, "onUpDownButton, destIndexEntry=" + dest.token); - searchText.removeTextChangedListener(searchTextWatcher); - searchText.setText(dest.token); - if (searchText.getLayout() != null) { - // Surprising, but this can otherwise crash sometimes... - Selection.moveToRightEdge(searchText.getText(), searchText.getLayout()); - } + setSearchText(dest.token, false); + jumpToRow(index.sortedIndexEntries.get(destIndexEntry).startRow); + defocusSearchText(); + } + + void onRandomWordButton() { + int destIndexEntry = rand.nextInt(index.sortedIndexEntries.size()); + final Index.IndexEntry dest = index.sortedIndexEntries.get(destIndexEntry); + setSearchText(dest.token, false); jumpToRow(index.sortedIndexEntries.get(destIndexEntry).startRow); - searchText.addTextChangedListener(searchTextWatcher); + defocusSearchText(); } // -------------------------------------------------------------------------- @@ -608,25 +835,51 @@ public class DictionaryActivity extends ListActivity { @Override public boolean onCreateOptionsMenu(final Menu menu) { - application.onCreateGlobalOptionsMenu(this, menu); - { - final MenuItem randomWord = menu.add(getString(R.string.randomWord)); - randomWord.setOnMenuItemClickListener(new OnMenuItemClickListener() { - public boolean onMenuItemClick(final MenuItem menuItem) { - final String word = index.sortedIndexEntries.get(random - .nextInt(index.sortedIndexEntries.size())).token; - setSearchText(word, true); - return false; + if (PreferenceManager.getDefaultSharedPreferences(this) + .getBoolean(getString(R.string.showPrevNextButtonsKey), true)) { + // Next word. + nextWordMenuItem = menu.add(getString(R.string.nextWord)) + .setIcon(R.drawable.arrow_down_float); + MenuItemCompat.setShowAsAction(nextWordMenuItem, MenuItem.SHOW_AS_ACTION_IF_ROOM); + nextWordMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + onUpDownButton(false); + return true; + } + }); + + // Previous word. + previousWordMenuItem = menu.add(getString(R.string.previousWord)) + .setIcon(R.drawable.arrow_up_float); + MenuItemCompat.setShowAsAction(previousWordMenuItem, MenuItem.SHOW_AS_ACTION_IF_ROOM); + previousWordMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + onUpDownButton(true); + return true; } }); } + randomWordMenuItem = menu.add(getString(R.string.randomWord)); + randomWordMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + onRandomWordButton(); + return true; + } + }); + + application.onCreateGlobalOptionsMenu(this, menu); + { - final MenuItem dictionaryList = menu.add(getString(R.string.dictionaryManager)); - dictionaryList.setOnMenuItemClickListener(new OnMenuItemClickListener() { + final MenuItem dictionaryManager = menu.add(getString(R.string.dictionaryManager)); + MenuItemCompat.setShowAsAction(dictionaryManager, MenuItem.SHOW_AS_ACTION_NEVER); + dictionaryManager.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(final MenuItem menuItem) { - startActivity(DictionaryManagerActivity.getLaunchIntent()); + startActivity(DictionaryManagerActivity.getLaunchIntent(getApplicationContext())); finish(); return false; } @@ -635,6 +888,7 @@ public class DictionaryActivity extends ListActivity { { final MenuItem aboutDictionary = menu.add(getString(R.string.aboutDictionary)); + MenuItemCompat.setShowAsAction(aboutDictionary, MenuItem.SHOW_AS_ACTION_NEVER); aboutDictionary.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(final MenuItem menuItem) { final Context context = getListView().getContext(); @@ -674,9 +928,6 @@ public class DictionaryActivity extends ListActivity { source.getNumEntries())).append("\n"); } } - // } else { - // builder.append(getString(R.string.invalidDictionary)); - // } textView.setText(builder.toString()); dialog.show(); @@ -701,18 +952,33 @@ public class DictionaryActivity extends ListActivity { AdapterContextMenuInfo adapterContextMenuInfo = (AdapterContextMenuInfo) menuInfo; final RowBase row = (RowBase) getListAdapter().getItem(adapterContextMenuInfo.position); - final MenuItem addToWordlist = menu.add(getString(R.string.addToWordList, + final android.view.MenuItem addToWordlist = menu.add(getString(R.string.addToWordList, wordList.getName())); - addToWordlist.setOnMenuItemClickListener(new OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - onAppendToWordList(row); + addToWordlist + .setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() { + public boolean onMenuItemClick(android.view.MenuItem item) { + onAppendToWordList(row); + return false; + } + }); + + final android.view.MenuItem share = menu.add("Share"); + share.setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() { + public boolean onMenuItemClick(android.view.MenuItem item) { + Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND); + shareIntent.setType("text/plain"); + shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, row.getTokenRow(true) + .getToken()); + shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, + row.getRawText(saveOnlyFirstSubentry)); + startActivity(shareIntent); return false; } }); - final MenuItem copy = menu.add(android.R.string.copy); - copy.setOnMenuItemClickListener(new OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { + final android.view.MenuItem copy = menu.add(android.R.string.copy); + copy.setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() { + public boolean onMenuItemClick(android.view.MenuItem item) { onCopy(row); return false; } @@ -720,50 +986,85 @@ public class DictionaryActivity extends ListActivity { if (selectedSpannableText != null) { final String selectedText = selectedSpannableText; - final MenuItem searchForSelection = menu.add(getString(R.string.searchForSelection, + final android.view.MenuItem searchForSelection = menu.add(getString( + R.string.searchForSelection, selectedSpannableText)); - searchForSelection.setOnMenuItemClickListener(new OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - int indexToUse = -1; - for (int i = 0; i < dictionary.indices.size(); ++i) { - final Index index = dictionary.indices.get(i); - if (indexPrepFinished) { - System.out.println("Doing index lookup: on " + selectedText); - final IndexEntry indexEntry = index.findExact(selectedText); - if (indexEntry != null) { - final TokenRow tokenRow = index.rows.get(indexEntry.startRow) - .getTokenRow(false); - if (tokenRow != null && tokenRow.hasMainEntry) { - indexToUse = i; - break; - } - } - } else { - Log.w(LOG, "Skipping findExact on index " + index.shortName); + searchForSelection + .setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() { + public boolean onMenuItemClick(android.view.MenuItem item) { + jumpToTextFromHyperLink(selectedText, selectedSpannableIndex); + return false; } - } - if (indexToUse == -1) { - indexToUse = selectedSpannableIndex; - } - final boolean changeIndex = indexIndex != indexToUse; - setSearchText(selectedText, !changeIndex); // If we're not - // changing - // index, we have - // to - // triggerSearch. - if (changeIndex) { - changeIndexGetFocusAndResearch(indexToUse); - } - // Give focus back to list view because typing is done. - getListView().requestFocus(); + }); + // Rats, this won't be shown: + //searchForSelection.setIcon(R.drawable.abs__ic_search); + } + + if ((row instanceof TokenRow || selectedSpannableText != null) && ttsReady) { + final android.view.MenuItem speak = menu.add(R.string.speak); + final String textToSpeak = row instanceof TokenRow ? ((TokenRow) row).getToken() : selectedSpannableText; + updateTTSLanguage(row instanceof TokenRow ? indexIndex : selectedSpannableIndex); + speak.setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(android.view.MenuItem item) { + textToSpeech.speak(textToSpeak, TextToSpeech.QUEUE_FLUSH, + new HashMap()); return false; } }); } + } + private void jumpToTextFromHyperLink( + final String selectedText, final int defaultIndexToUse) { + int indexToUse = -1; + int numFound = 0; + for (int i = 0; i < dictionary.indices.size(); ++i) { + final Index index = dictionary.indices.get(i); + if (indexPrepFinished) { + System.out.println("Doing index lookup: on " + selectedText); + final IndexEntry indexEntry = index.findExact(selectedText); + if (indexEntry != null) { + final TokenRow tokenRow = index.rows.get(indexEntry.startRow) + .getTokenRow(false); + if (tokenRow != null && tokenRow.hasMainEntry) { + indexToUse = i; + ++numFound; + } + } + } else { + Log.w(LOG, "Skipping findExact on index " + index.shortName); + } + } + if (numFound != 1 || indexToUse == -1) { + indexToUse = defaultIndexToUse; + } + // Without this extra delay, the call to jumpToRow that this + // invokes doesn't always actually have any effect. + final int actualIndexToUse = indexToUse; + getListView().postDelayed(new Runnable() { + @Override + public void run() { + setIndexAndSearchText(actualIndexToUse, selectedText, true); + } + }, 100); + } + + /** + * Called when user clicks outside of search text, so that they can start + * typing again immediately. + */ + void defocusSearchText() { + // Log.d(LOG, "defocusSearchText"); + // Request focus so that if we start typing again, it clears the text + // input. + getListView().requestFocus(); + + // Visual indication that a new keystroke will clear the search text. + // Doesn't seem to work unless searchText has focus. + // searchView.selectAll(); } - @Override protected void onListItemClick(ListView l, View v, int row, long id) { defocusSearchText(); if (clickOpensContextMenu && dictRaf != null) { @@ -771,6 +1072,7 @@ public class DictionaryActivity extends ListActivity { } } + @SuppressLint("SimpleDateFormat") void onAppendToWordList(final RowBase row) { defocusSearchText(); @@ -786,7 +1088,7 @@ public class DictionaryActivity extends ListActivity { final PrintWriter out = new PrintWriter(new FileWriter(wordList, true)); out.println(rawText.toString()); out.close(); - } catch (IOException e) { + } catch (Exception e) { Log.e(LOG, "Unable to append to " + wordList.getAbsolutePath(), e); Toast.makeText(this, getString(R.string.failedAddingToWordList, wordList.getAbsolutePath()), @@ -795,20 +1097,6 @@ public class DictionaryActivity extends ListActivity { return; } - /** - * Called when user clicks outside of search text, so that they can start - * typing again immediately. - */ - void defocusSearchText() { - // Log.d(LOG, "defocusSearchText"); - // Request focus so that if we start typing again, it clears the text - // input. - getListView().requestFocus(); - - // Visual indication that a new keystroke will clear the search text. - searchText.selectAll(); - } - @SuppressWarnings("deprecation") void onCopy(final RowBase row) { defocusSearchText(); @@ -824,8 +1112,9 @@ public class DictionaryActivity extends ListActivity { @Override public boolean onKeyDown(final int keyCode, final KeyEvent event) { if (event.getUnicodeChar() != 0) { - if (!searchText.hasFocus()) { + if (!searchView.hasFocus()) { setSearchText("" + (char) event.getUnicodeChar(), true); + searchView.requestFocus(); } return true; } @@ -838,41 +1127,72 @@ public class DictionaryActivity extends ListActivity { if (keyCode == KeyEvent.KEYCODE_ENTER) { Log.d(LOG, "Trying to hide soft keyboard."); final InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - inputManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), - InputMethodManager.HIDE_NOT_ALWAYS); + View focus = getCurrentFocus(); + if (focus != null) { + inputManager.hideSoftInputFromWindow(focus.getWindowToken(), + InputMethodManager.HIDE_NOT_ALWAYS); + } return true; } return super.onKeyDown(keyCode, event); } - private void setSearchText(final String text, final boolean triggerSearch) { - if (!triggerSearch) { - getListView().requestFocus(); + private void setIndexAndSearchText(int newIndex, String newSearchText, boolean hideKeyboard) { + Log.d(LOG, "Changing index to: " + newIndex); + if (newIndex == -1) { + Log.e(LOG, "Invalid index."); + newIndex = 0; + } + if (newIndex != indexIndex) { + indexIndex = newIndex; + index = dictionary.indices.get(indexIndex); + indexAdapter = new IndexAdapter(index); + setListAdapter(indexAdapter); + Log.d(LOG, "changingIndex, newLang=" + index.longName); + setDictionaryPrefs(this, dictFile, index.shortName, searchView.getQuery().toString()); + updateLangButton(); } - searchText.setText(text); - searchText.requestFocus(); + setSearchText(newSearchText, true, hideKeyboard); + } + + private void setSearchText(final String text, final boolean triggerSearch, boolean hideKeyboard) { + Log.d(LOG, "setSearchText, text=" + text + ", triggerSearch=" + triggerSearch); + // Disable the listener, because sometimes it doesn't work. + searchView.setOnQueryTextListener(null); + searchView.setQuery(text, false); moveCursorToRight(); + searchView.setOnQueryTextListener(onQueryTextListener); + if (triggerSearch) { onSearchTextChange(text); } + + // We don't want to show virtual keyboard when we're changing searchView text programatically: + if (hideKeyboard) { + hideKeyboard(); + } } - private long cursorDelayMillis = 100; + private void setSearchText(final String text, final boolean triggerSearch) { + setSearchText(text, triggerSearch, true); + } + // private long cursorDelayMillis = 100; private void moveCursorToRight() { - if (searchText.getLayout() != null) { - cursorDelayMillis = 100; - // Surprising, but this can crash when you rotate... - Selection.moveToRightEdge(searchText.getText(), searchText.getLayout()); - } else { - uiHandler.postDelayed(new Runnable() { - @Override - public void run() { - moveCursorToRight(); - } - }, cursorDelayMillis); - cursorDelayMillis = Math.min(10 * 1000, 2 * cursorDelayMillis); - } + // if (searchText.getLayout() != null) { + // cursorDelayMillis = 100; + // // Surprising, but this can crash when you rotate... + // Selection.moveToRightEdge(searchView.getQuery(), + // searchText.getLayout()); + // } else { + // uiHandler.postDelayed(new Runnable() { + // @Override + // public void run() { + // moveCursorToRight(); + // } + // }, cursorDelayMillis); + // cursorDelayMillis = Math.min(10 * 1000, 2 * cursorDelayMillis); + // } } // -------------------------------------------------------------------------- @@ -913,11 +1233,12 @@ public class DictionaryActivity extends ListActivity { } } }, 20); - } private final void jumpToRow(final int row) { - setSelection(row); + Log.d(LOG, "jumpToRow: " + row + ", refocusSearchText=" + false); + // getListView().requestFocusFromTouch(); + getListView().setSelectionFromTop(row, 0); getListView().setSelected(true); } @@ -942,7 +1263,7 @@ public class DictionaryActivity extends ListActivity { boolean done = false; SearchOperation(final String searchText, final Index index) { - this.searchText = searchText.trim(); + this.searchText = StringUtil.normalizeWhitespace(searchText); this.index = index; } @@ -959,7 +1280,8 @@ public class DictionaryActivity extends ListActivity { searchResult = index.findInsertionPoint(searchText, interrupted); } else { searchTokens = Arrays.asList(searchTokenArray); - multiWordSearchResult = index.multiWordSearch(searchTokens, interrupted); + multiWordSearchResult = index.multiWordSearch(searchText, searchTokens, + interrupted); } Log.d(LOG, "searchText=" + searchText + ", searchDuration=" @@ -972,6 +1294,8 @@ public class DictionaryActivity extends ListActivity { searchFinished(SearchOperation.this); } }); + } else { + Log.d(LOG, "interrupted, skipping searchFinished."); } } catch (Exception e) { Log.e(LOG, "Failure during search (can happen during Activity close."); @@ -1150,7 +1474,10 @@ public class DictionaryActivity extends ListActivity { result.setClickable(true); result.setFocusable(true); result.setLongClickable(true); - result.setBackgroundResource(android.R.drawable.menuitem_background); +// result.setBackgroundResource(android.R.drawable.menuitem_background); + + result.setBackgroundResource(theme.normalRowBg); + result.setOnClickListener(new TextView.OnClickListener() { @Override public void onClick(View v) { @@ -1161,87 +1488,85 @@ public class DictionaryActivity extends ListActivity { return result; } - private TableLayout getView(HtmlEntry.Row row, ViewGroup parent, final TableLayout result) { + private TableLayout getPossibleLinkToHtmlEntryView(final boolean isTokenRow, + final String text, final boolean hasMainEntry, final List htmlEntries, + final String htmlTextToHighlight, ViewGroup parent, final TableLayout result) { final Context context = parent.getContext(); - final HtmlEntry htmlEntry = row.getEntry(); - - // final TableRow tableRow = new TableRow(context); - final LinearLayout tableRow = new LinearLayout(context); - result.addView(tableRow); - - // Text. - final TextView textView = new TextView(context); - textView.setText(htmlEntry.title); - textView.setLayoutParams(new LinearLayout.LayoutParams(0, - ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f)); - tableRow.addView(textView); - - // Button. - final Button button = new Button(context); - button.setText("open"); - button.setLayoutParams(new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0.0f)); - tableRow.addView(button); - - button.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - startActivity(HtmlDisplayActivity.getHtmlIntent(String.format( - "%s", htmlEntry.html))); - } - }); - - return result; - } - - private TableLayout getView(TokenRow row, ViewGroup parent, final TableLayout result) { - final Context context = parent.getContext(); - final TableRow tableRow = new TableRow(result.getContext()); - tableRow.setBackgroundResource(row.hasMainEntry ? theme.tokenRowMainBg + tableRow.setBackgroundResource(hasMainEntry ? theme.tokenRowMainBg : theme.tokenRowOtherBg); - tableRow.setPadding(mPaddingDefault, mPaddingDefault, mPaddingDefault, 0); + if (isTokenRow) { + tableRow.setPadding(mPaddingDefault, mPaddingDefault, mPaddingDefault, 0); + } else { + tableRow.setPadding(mPaddingLarge, mPaddingDefault, mPaddingDefault, 0); + } result.addView(tableRow); - - final IndexEntry indexEntry = row.getIndexEntry(); + // Make it so we can long-click on these token rows, too: final TextView textView = new TextView(context); - textView.setText(indexEntry.token); + textView.setText(text, BufferType.SPANNABLE); + createTokenLinkSpans(textView, (Spannable) textView.getText(), text); + textView.setOnLongClickListener(indexIndex > 0 ? textViewLongClickListenerIndex1 : textViewLongClickListenerIndex0); + result.setLongClickable(true); + // Doesn't work: // textView.setTextColor(android.R.color.secondary_text_light); - textView.setTextAppearance(context, theme.tokenRowFg); textView.setTypeface(typeface); - textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 5 * fontSizeSp / 4); TableRow.LayoutParams lp = new TableRow.LayoutParams(0); + if (isTokenRow) { + textView.setTextAppearance(context, theme.tokenRowFg); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 4 * fontSizeSp / 3); + } else { + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSizeSp); + } lp.weight = 1.0f; + textView.setLayoutParams(lp); tableRow.addView(textView); - - if (!indexEntry.htmlEntries.isEmpty()) { - final ImageButton button = new ImageButton(context); - button.setImageResource(R.drawable.ic_menu_forward); - button.setOnClickListener(new OnClickListener() { + if (!htmlEntries.isEmpty()) { + final ClickableSpan clickableSpan = new ClickableSpan() { + @Override + public void onClick(View widget) { + } + }; + ((Spannable) textView.getText()).setSpan(clickableSpan, 0, text.length(), + Spannable.SPAN_INCLUSIVE_INCLUSIVE); + result.setClickable(true); + textView.setClickable(true); + textView.setMovementMethod(LinkMovementMethod.getInstance()); + textView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - final String html = HtmlEntry.htmlBody(indexEntry.htmlEntries); - startActivity(HtmlDisplayActivity.getHtmlIntent(String.format( - "%s", html))); + String html = HtmlEntry.htmlBody(htmlEntries, index.shortName); + // Log.d(LOG, "html=" + html); + startActivityForResult( + HtmlDisplayActivity.getHtmlIntent(getApplicationContext(), String.format( + "%s", html), + htmlTextToHighlight, false), + 0); } }); - tableRow.addView(button); - lp = new TableRow.LayoutParams(1); - lp.weight = 0.0f; - button.setLayoutParams(lp); - //result.setColumnStretchable(0, true); - //result.setColumnStretchable(1, false); } - - return result; } + private TableLayout getView(TokenRow row, ViewGroup parent, final TableLayout result) { + final IndexEntry indexEntry = row.getIndexEntry(); + return getPossibleLinkToHtmlEntryView(true, indexEntry.token, row.hasMainEntry, + indexEntry.htmlEntries, null, parent, result); + } + + private TableLayout getView(HtmlEntry.Row row, ViewGroup parent, final TableLayout result) { + final HtmlEntry htmlEntry = row.getEntry(); + final TokenRow tokenRow = row.getTokenRow(true); + return getPossibleLinkToHtmlEntryView(false, + getString(R.string.seeAlso, htmlEntry.title, htmlEntry.entrySource.getName()), + false, Collections.singletonList(htmlEntry), tokenRow.getToken(), parent, + result); + } + } static final Pattern CHAR_DASH = Pattern.compile("['\\p{L}\\p{M}\\p{N}]+"); @@ -1253,7 +1578,8 @@ public class DictionaryActivity extends ListActivity { textView.setMovementMethod(LinkMovementMethod.getInstance()); final Matcher matcher = CHAR_DASH.matcher(text); while (matcher.find()) { - spannable.setSpan(new NonLinkClickableSpan(), matcher.start(), matcher.end(), + spannable.setSpan(new NonLinkClickableSpan(textColorFg), matcher.start(), + matcher.end(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); } } @@ -1319,10 +1645,11 @@ public class DictionaryActivity extends ListActivity { Log.d(LOG, "searchText changed during shutdown, doing nothing."); return; } - if (!searchText.isFocused()) { - Log.d(LOG, "searchText changed without focus, doing nothing."); - return; - } + + // if (!searchView.hasFocus()) { + // Log.d(LOG, "searchText changed without focus, doing nothing."); + // return; + // } Log.d(LOG, "onSearchTextChange: " + text); if (currentSearchOperation != null) { Log.d(LOG, "Interrupting currentSearchOperation."); @@ -1332,22 +1659,6 @@ public class DictionaryActivity extends ListActivity { searchExecutor.execute(currentSearchOperation); } - private class SearchTextWatcher implements TextWatcher { - public void afterTextChanged(final Editable searchTextEditable) { - if (searchText.hasFocus()) { - Log.d(LOG, "Search text changed with focus: " + searchText.getText()); - // If they were typing to cause the change, update the UI. - onSearchTextChange(searchText.getText().toString()); - } - } - - public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { - } - - public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { - } - } - // -------------------------------------------------------------------------- // Filtered results. // -------------------------------------------------------------------------- @@ -1357,15 +1668,19 @@ public class DictionaryActivity extends ListActivity { } void setFiltered(final SearchOperation searchOperation) { - ((Button) findViewById(R.id.UpButton)).setEnabled(false); - ((Button) findViewById(R.id.DownButton)).setEnabled(false); + if (nextWordMenuItem != null) { + nextWordMenuItem.setEnabled(false); + previousWordMenuItem.setEnabled(false); + } rowsToShow = searchOperation.multiWordSearchResult; setListAdapter(new IndexAdapter(index, rowsToShow, searchOperation.searchTokens)); } void clearFiltered() { - ((Button) findViewById(R.id.UpButton)).setEnabled(true); - ((Button) findViewById(R.id.DownButton)).setEnabled(true); + if (nextWordMenuItem != null) { + nextWordMenuItem.setEnabled(true); + previousWordMenuItem.setEnabled(true); + } setListAdapter(new IndexAdapter(index)); rowsToShow = null; }