X-Git-Url: http://gitweb.fperrin.net/?a=blobdiff_plain;f=src%2Fcom%2Fhughes%2Fandroid%2Fdictionary%2FDictionaryActivity.java;h=02286155532c187bf366a1cf86ffcdec1e034db6;hb=e79165503392ed6a7cb7a8eadc15eaae0cda9443;hp=14aa4ff15d0a00375771bfe9aefc18cfc884a136;hpb=fd81d71fac2fa1c1a456ae4ab51783ebcd260b18;p=Dictionary.git diff --git a/src/com/hughes/android/dictionary/DictionaryActivity.java b/src/com/hughes/android/dictionary/DictionaryActivity.java index 14aa4ff..0228615 100644 --- a/src/com/hughes/android/dictionary/DictionaryActivity.java +++ b/src/com/hughes/android/dictionary/DictionaryActivity.java @@ -29,16 +29,18 @@ import android.os.Handler; import android.preference.PreferenceManager; import android.speech.tts.TextToSpeech; import android.speech.tts.TextToSpeech.OnInitListener; +import android.support.annotation.NonNull; import android.support.design.widget.FloatingActionButton; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; 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.InputType; import android.text.Spannable; +import android.text.SpannableString; import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; import android.text.style.StyleSpan; @@ -75,7 +77,6 @@ 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; @@ -84,7 +85,6 @@ 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; @@ -110,7 +110,6 @@ import java.util.List; import java.util.Locale; import java.util.Random; import java.util.Set; -import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; @@ -118,44 +117,44 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class DictionaryActivity extends ActionBarActivity { +public class DictionaryActivity extends AppCompatActivity { - static final String LOG = "QuickDic"; + private static final String LOG = "QuickDic"; - DictionaryApplication application; + private DictionaryApplication application; - File dictFile = null; - FileChannel dictRaf = null; - String dictFileTitleName = null; + private File dictFile = null; + private FileChannel dictRaf = null; + private String dictFileTitleName = null; - Dictionary dictionary = null; + private Dictionary dictionary = null; - int indexIndex = 0; + private int indexIndex = 0; - Index index = null; + private Index index = null; - List rowsToShow = null; // if not null, just show these rows. + private List rowsToShow = null; // if not null, just show these rows. - final Random rand = new Random(); + private final Random rand = new Random(); - final Handler uiHandler = new Handler(); + private final Handler uiHandler = new Handler(); private final ExecutorService searchExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() { @Override - public Thread newThread(Runnable r) { + public Thread newThread(@NonNull Runnable r) { return new Thread(r, "searchExecutor"); } }); private SearchOperation currentSearchOperation = null; - TextToSpeech textToSpeech; - volatile boolean ttsReady; + private TextToSpeech textToSpeech; + private volatile boolean ttsReady; - Typeface typeface; - DictionaryApplication.Theme theme = DictionaryApplication.Theme.LIGHT; - int textColorFg = Color.BLACK; - int fontSizeSp; + private Typeface typeface; + private DictionaryApplication.Theme theme = DictionaryApplication.Theme.LIGHT; + private int textColorFg = Color.BLACK; + private int fontSizeSp; private ListView listView; private ListView getListView() { @@ -173,11 +172,12 @@ public class DictionaryActivity extends ActionBarActivity { return getListView().getAdapter(); } - SearchView searchView; - ImageButton languageButton; - SearchView.OnQueryTextListener onQueryTextListener; + private SearchView searchView; + private ImageButton languageButton; + private SearchView.OnQueryTextListener onQueryTextListener; - MenuItem nextWordMenuItem, previousWordMenuItem, randomWordMenuItem; + private MenuItem nextWordMenuItem; + private MenuItem previousWordMenuItem; // Never null. private File wordList = null; @@ -185,7 +185,7 @@ public class DictionaryActivity extends ActionBarActivity { private boolean clickOpensContextMenu = false; // Visible for testing. - ListAdapter indexAdapter = null; + private ListAdapter indexAdapter = null; /** * For some languages, loading the transliterators used in this search takes @@ -229,6 +229,8 @@ public class DictionaryActivity extends ActionBarActivity { private void dictionaryOpenFail(Exception e) { Log.e(LOG, "Unable to load dictionary.", e); if (dictRaf != null) { + indexAdapter = null; + setListAdapter(null); try { dictRaf.close(); } catch (IOException e1) { @@ -236,8 +238,9 @@ public class DictionaryActivity extends ActionBarActivity { } dictRaf = null; } - Toast.makeText(this, getString(R.string.invalidDictionary, "", e.getMessage()), - Toast.LENGTH_LONG).show(); + if (!isFinishing()) + Toast.makeText(this, getString(R.string.invalidDictionary, "", e.getMessage()), + Toast.LENGTH_LONG).show(); startActivity(DictionaryManagerActivity.getLaunchIntent(getApplicationContext())); finish(); } @@ -257,7 +260,7 @@ public class DictionaryActivity extends ActionBarActivity { final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); // Don't auto-launch if this fails. - prefs.edit().remove(C.DICT_FILE).commit(); + prefs.edit().remove(C.DICT_FILE).remove(C.INDEX_SHORT_NAME).commit(); setContentView(R.layout.dictionary_activity); @@ -275,8 +278,8 @@ public class DictionaryActivity extends ActionBarActivity { final Intent intent = getIntent(); String intentAction = intent.getAction(); - /** - * @author Dominik Köppl Querying the Intent + /* + @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 @@ -325,8 +328,8 @@ public class DictionaryActivity extends ActionBarActivity { } } - /** - * @author Dominik Köppl Querying the Intent Intent.ACTION_SEARCH is a + /* + @author Dominik Köppl Querying the Intent Intent.ACTION_SEARCH is a * simple query Arguments follow from android standard (see * documentation) */ @@ -361,8 +364,8 @@ public class DictionaryActivity extends ActionBarActivity { return; } } - /** - * @author Dominik Köppl If no dictionary is chosen, use the default + /* + @author Dominik Köppl If no dictionary is chosen, use the default * dictionary specified in the preferences If this step does * fail (no default dictionary specified), show a toast and * abort. @@ -411,12 +414,13 @@ public class DictionaryActivity extends ActionBarActivity { } if (dictFilename == null) { - Toast.makeText(this, getString(R.string.no_dict_file), Toast.LENGTH_LONG).show(); + if (!isFinishing()) + Toast.makeText(this, getString(R.string.no_dict_file), Toast.LENGTH_LONG).show(); startActivity(DictionaryManagerActivity.getLaunchIntent(getApplicationContext())); finish(); return; } - if (dictRaf == null && dictFilename != null) + if (dictRaf == null) dictFile = new File(dictFilename); ttsReady = false; @@ -499,25 +503,32 @@ public class DictionaryActivity extends ActionBarActivity { }).start(); 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) { - Log.w(LOG, "Exception trying to use typeface, using default.", e); - Toast.makeText(this, getString(R.string.fontFailure, e.getLocalizedMessage()), - Toast.LENGTH_LONG).show(); - } + switch (fontName) { + case "SYSTEM": + typeface = Typeface.DEFAULT; + break; + case "SERIF": + typeface = Typeface.SERIF; + break; + case "SANS_SERIF": + typeface = Typeface.SANS_SERIF; + break; + case "MONOSPACE": + typeface = Typeface.MONOSPACE; + break; + default: + if ("FreeSerif.ttf.jpg".equals(fontName)) { + fontName = "FreeSerif.otf.jpg"; + } + try { + typeface = Typeface.createFromAsset(getAssets(), fontName); + } catch (Exception e) { + Log.w(LOG, "Exception trying to use typeface, using default.", e); + if (!isFinishing()) + Toast.makeText(this, getString(R.string.fontFailure, e.getLocalizedMessage()), + Toast.LENGTH_LONG).show(); + } + break; } if (typeface == null) { Log.w(LOG, "Unable to create typeface, using default."); @@ -563,7 +574,7 @@ public class DictionaryActivity extends ActionBarActivity { setSearchText(text, true); Log.d(LOG, "Trying to restore searchText=" + text); - setDictionaryPrefs(this, dictFile, index.shortName, searchView.getQuery().toString()); + setDictionaryPrefs(this, dictFile, index.shortName); updateLangButton(); searchView.requestFocus(); @@ -659,16 +670,11 @@ public class DictionaryActivity extends ActionBarActivity { showKeyboard(); } - @Override - protected void onPause() { - super.onPause(); - } - - @Override /** * Invoked when MyWebView returns, since the user might have clicked some * hypertext in the MyWebView. */ + @Override protected void onActivityResult(int requestCode, int resultCode, Intent result) { super.onActivityResult(requestCode, resultCode, result); if (result != null && result.hasExtra(C.SEARCH_TOKEN)) { @@ -678,14 +684,14 @@ public class DictionaryActivity extends ActionBarActivity { } private static void setDictionaryPrefs(final Context context, final File dictFile, - final String indexShortName, final String searchToken) { + final String indexShortName) { final SharedPreferences.Editor prefs = PreferenceManager.getDefaultSharedPreferences( context).edit(); if (dictFile != null) { prefs.putString(C.DICT_FILE, dictFile.getPath()); prefs.putString(C.INDEX_SHORT_NAME, indexShortName); } - prefs.putString(C.SEARCH_TOKEN, ""); // Don't need to save search token. + prefs.remove(C.SEARCH_TOKEN); // Don't need to save search token. prefs.commit(); } @@ -709,6 +715,9 @@ public class DictionaryActivity extends ActionBarActivity { textToSpeech.shutdown(); textToSpeech = null; + indexAdapter = null; + setListAdapter(null); + try { Log.d(LOG, "Closing RAF."); dictRaf.close(); @@ -750,7 +759,7 @@ public class DictionaryActivity extends ActionBarActivity { manager.hideSoftInputFromWindow(searchView.getWindowToken(), 0); } - void updateLangButton() { + private void updateLangButton() { final int flagId = IsoUtils.INSTANCE.getFlagIdForIsoCode(index.shortName); if (flagId != 0) { languageButton.setImageResource(flagId); @@ -778,7 +787,8 @@ public class DictionaryActivity extends ActionBarActivity { 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(); + if (!isFinishing()) + Toast.makeText(this, getString(R.string.TTSbroken), Toast.LENGTH_LONG).show(); } } @@ -806,14 +816,14 @@ public class DictionaryActivity extends ActionBarActivity { searchView.getQuery().toString(), false); } - void onLanguageButtonLongClick(final Context context) { + private void onLanguageButtonLongClick(final Context context) { final Dialog dialog = new Dialog(context); dialog.setContentView(R.layout.select_dictionary_dialog); dialog.setTitle(R.string.selectDictionary); final List installedDicts = application.getDictionariesOnDevice(null); - ListView listView = (ListView) dialog.findViewById(android.R.id.list); + ListView listView = dialog.findViewById(android.R.id.list); final Button button = new Button(listView.getContext()); final String name = getString(R.string.dictionaryManager); button.setText(name); @@ -836,10 +846,10 @@ public class DictionaryActivity extends ActionBarActivity { final LinearLayout result = new LinearLayout(parent.getContext()); - for (int i = 0; dictionaryInfo.indexInfos != null && i < dictionaryInfo.indexInfos.size(); ++i) { + for (int i = 0; i < dictionaryInfo.indexInfos.size(); ++i) { final IndexInfo indexInfo = dictionaryInfo.indexInfos.get(i); final View button = IsoUtils.INSTANCE.createButton(parent.getContext(), - dictionaryInfo, indexInfo, application.languageButtonPixels); + indexInfo, application.languageButtonPixels); final IntentLauncher intentLauncher = new IntentLauncher(parent.getContext(), getLaunchIntent(getApplicationContext(), application.getPath(dictionaryInfo.uncompressedFilename), @@ -890,7 +900,7 @@ public class DictionaryActivity extends ActionBarActivity { dialog.show(); } - void onUpDownButton(final boolean up) { + private void onUpDownButton(final boolean up) { if (isFiltered()) { return; } @@ -915,7 +925,7 @@ public class DictionaryActivity extends ActionBarActivity { defocusSearchText(); } - void onRandomWordButton() { + private void onRandomWordButton() { int destIndexEntry = rand.nextInt(index.sortedIndexEntries.size()); final Index.IndexEntry dest = index.sortedIndexEntries.get(destIndexEntry); setSearchText(dest.token, false); @@ -927,8 +937,6 @@ public class DictionaryActivity extends ActionBarActivity { // Options Menu // -------------------------------------------------------------------------- - final Random random = new Random(); - @Override public boolean onCreateOptionsMenu(final Menu menu) { @@ -959,7 +967,7 @@ public class DictionaryActivity extends ActionBarActivity { }); } - randomWordMenuItem = menu.add(getString(R.string.randomWord)); + final MenuItem randomWordMenuItem = menu.add(getString(R.string.randomWord)); randomWordMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { @@ -968,8 +976,6 @@ public class DictionaryActivity extends ActionBarActivity { } }); - application.onCreateGlobalOptionsMenu(this, menu); - { final MenuItem dictionaryManager = menu.add(getString(R.string.dictionaryManager)); MenuItemCompat.setShowAsAction(dictionaryManager, MenuItem.SHOW_AS_ACTION_NEVER); @@ -990,7 +996,7 @@ public class DictionaryActivity extends ActionBarActivity { final Context context = getListView().getContext(); final Dialog dialog = new Dialog(context); dialog.setContentView(R.layout.about_dictionary_dialog); - final TextView textView = (TextView) dialog.findViewById(R.id.text); + final TextView textView = dialog.findViewById(R.id.text); dialog.setTitle(dictFileTitleName); @@ -1040,6 +1046,8 @@ public class DictionaryActivity extends ActionBarActivity { }); } + DictionaryApplication.onCreateGlobalOptionsMenu(this, menu); + return true; } @@ -1055,7 +1063,7 @@ public class DictionaryActivity extends ActionBarActivity { if (clickOpensContextMenu && (row instanceof HtmlEntry.Row || (row instanceof TokenRow && ((TokenRow)row).getIndexEntry().htmlEntries.size() > 0))) { final List html = row instanceof TokenRow ? ((TokenRow)row).getIndexEntry().htmlEntries : Collections.singletonList(((HtmlEntry.Row)row).getEntry()); - final String highlight = row instanceof HtmlEntry.Row ? ((HtmlEntry.Row)row).getTokenRow(true).getToken() : null; + final String highlight = row instanceof HtmlEntry.Row ? row.getTokenRow(true).getToken() : null; final MenuItem open = menu.add("Open"); open.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { @@ -1180,7 +1188,7 @@ public class DictionaryActivity extends ActionBarActivity { Log.w(LOG, "Skipping findExact on index " + index.shortName); } } - if (numFound != 1 || indexToUse == -1) { + if (numFound != 1) { indexToUse = defaultIndexToUse; } // Without this extra delay, the call to jumpToRow that this @@ -1198,7 +1206,7 @@ public class DictionaryActivity extends ActionBarActivity { * Called when user clicks outside of search text, so that they can start * typing again immediately. */ - void defocusSearchText() { + private void defocusSearchText() { // Log.d(LOG, "defocusSearchText"); // Request focus so that if we start typing again, it clears the text // input. @@ -1209,7 +1217,7 @@ public class DictionaryActivity extends ActionBarActivity { // searchView.selectAll(); } - protected void onListItemClick(ListView l, View v, int rowIdx, long id) { + private void onListItemClick(ListView l, View v, int rowIdx, long id) { defocusSearchText(); if (clickOpensContextMenu && dictRaf != null) { openContextMenu(v); @@ -1222,7 +1230,7 @@ public class DictionaryActivity extends ActionBarActivity { } @SuppressLint("SimpleDateFormat") - void onAppendToWordList(final RowBase row) { + private void onAppendToWordList(final RowBase row) { defocusSearchText(); final StringBuilder rawText = new StringBuilder(); @@ -1239,15 +1247,15 @@ public class DictionaryActivity extends ActionBarActivity { out.close(); } catch (Exception e) { Log.e(LOG, "Unable to append to " + wordList.getAbsolutePath(), e); - Toast.makeText(this, - getString(R.string.failedAddingToWordList, wordList.getAbsolutePath()), - Toast.LENGTH_LONG).show(); + if (!isFinishing()) + Toast.makeText(this, + getString(R.string.failedAddingToWordList, wordList.getAbsolutePath()), + Toast.LENGTH_LONG).show(); } - return; } @SuppressWarnings("deprecation") - void onCopy(final RowBase row) { + private void onCopy(final RowBase row) { defocusSearchText(); Log.d(LOG, "Copy, row=" + row); @@ -1277,7 +1285,7 @@ public class DictionaryActivity extends ActionBarActivity { Log.d(LOG, "Trying to hide soft keyboard."); final InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); View focus = getCurrentFocus(); - if (focus != null) { + if (inputManager != null && focus != null) { inputManager.hideSoftInputFromWindow(focus.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } @@ -1298,7 +1306,7 @@ public class DictionaryActivity extends ActionBarActivity { indexAdapter = new IndexAdapter(index); setListAdapter(indexAdapter); Log.d(LOG, "changingIndex, newLang=" + index.longName); - setDictionaryPrefs(this, dictFile, index.shortName, searchView.getQuery().toString()); + setDictionaryPrefs(this, dictFile, index.shortName); updateLangButton(); } setSearchText(newSearchText, true, hideKeyboard); @@ -1309,7 +1317,6 @@ public class DictionaryActivity extends ActionBarActivity { // Disable the listener, because sometimes it doesn't work. searchView.setOnQueryTextListener(null); searchView.setQuery(text, false); - moveCursorToRight(); searchView.setOnQueryTextListener(onQueryTextListener); if (triggerSearch) { @@ -1326,24 +1333,6 @@ public class DictionaryActivity extends ActionBarActivity { 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(searchView.getQuery(), - // searchText.getLayout()); - // } else { - // uiHandler.postDelayed(new Runnable() { - // @Override - // public void run() { - // moveCursorToRight(); - // } - // }, cursorDelayMillis); - // cursorDelayMillis = Math.min(10 * 1000, 2 * cursorDelayMillis); - // } - } - // -------------------------------------------------------------------------- // SearchOperation // -------------------------------------------------------------------------- @@ -1391,7 +1380,7 @@ public class DictionaryActivity extends ActionBarActivity { getListView().setSelected(true); } - static final Pattern WHITESPACE = Pattern.compile("\\s+"); + private static final Pattern WHITESPACE = Pattern.compile("\\s+"); final class SearchOperation implements Runnable { @@ -1447,7 +1436,7 @@ public class DictionaryActivity extends ActionBarActivity { Log.d(LOG, "interrupted, skipping searchFinished."); } } catch (Exception e) { - Log.e(LOG, "Failure during search (can happen during Activity close."); + Log.e(LOG, "Failure during search (can happen during Activity close): " + e.getMessage()); } finally { synchronized (this) { done = true; @@ -1471,12 +1460,6 @@ public class DictionaryActivity extends ActionBarActivity { 0); } - static ViewGroup.LayoutParams WEIGHT_1 = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT, 1.0f); - - static ViewGroup.LayoutParams WEIGHT_0 = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT, 0.0f); - final class IndexAdapter extends BaseAdapter { private static final float PADDING_DEFAULT_DP = 8; @@ -1503,7 +1486,7 @@ public class DictionaryActivity extends ActionBarActivity { IndexAdapter(final Index index, final List rows, final List toHighlight) { this.index = index; this.rows = rows; - this.toHighlight = new LinkedHashSet(toHighlight); + this.toHighlight = new LinkedHashSet<>(toHighlight); getMetrics(); } @@ -1518,7 +1501,7 @@ public class DictionaryActivity extends ActionBarActivity { DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); scale = dm.density; - } catch (NullPointerException e) + } catch (NullPointerException ignored) {} // Convert the dps to pixels, based on density scale mPaddingDefault = (int) (PADDING_DEFAULT_DP * scale + 0.5f); @@ -1541,36 +1524,75 @@ public class DictionaryActivity extends ActionBarActivity { } @Override - public TableLayout getView(int position, View convertView, ViewGroup parent) { - final TableLayout result; - if (convertView instanceof TableLayout) { - result = (TableLayout) convertView; - result.removeAllViews(); + public int getViewTypeCount() { + return 5; + } + + @Override + public int getItemViewType(int position) { + final RowBase row = getItem(position); + if (row instanceof PairEntry.Row) { + final PairEntry entry = ((PairEntry.Row)row).getEntry(); + final int rowCount = entry.pairs.size(); + return rowCount > 1 ? 1 : 0; + } else if (row instanceof TokenRow) { + final IndexEntry indexEntry = ((TokenRow)row).getIndexEntry(); + return indexEntry.htmlEntries.isEmpty() ? 2 : 3; + } else if (row instanceof HtmlEntry.Row) { + return 4; } else { - result = new TableLayout(parent.getContext()); + throw new IllegalArgumentException("Unsupported Row type: " + row.getClass()); } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { final RowBase row = getItem(position); if (row instanceof PairEntry.Row) { - return getView(position, (PairEntry.Row) row, parent, result); + return getView(position, (PairEntry.Row) row, parent, (TableLayout)convertView); } else if (row instanceof TokenRow) { - return getView((TokenRow) row, parent, result); + return getView((TokenRow) row, parent, (TextView)convertView); } else if (row instanceof HtmlEntry.Row) { - return getView((HtmlEntry.Row) row, parent, result); + return getView((HtmlEntry.Row) row, parent, (TextView)convertView); } else { throw new IllegalArgumentException("Unsupported Row type: " + row.getClass()); } } + private void addBoldSpans(String token, String col1Text, Spannable col1Spannable) { + int startPos = 0; + while ((startPos = col1Text.indexOf(token, startPos)) != -1) { + col1Spannable.setSpan(new StyleSpan(Typeface.BOLD), startPos, startPos + + token.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + startPos += token.length(); + } + } + private TableLayout getView(final int position, PairEntry.Row row, ViewGroup parent, - final TableLayout result) { + TableLayout result) { + final Context context = parent.getContext(); final PairEntry entry = row.getEntry(); final int rowCount = entry.pairs.size(); + if (result == null) { + result = new TableLayout(context); + result.setStretchAllColumns(true); + // Because we have a Button inside a ListView row: + // http://groups.google.com/group/android-developers/browse_thread/thread/3d96af1530a7d62a?pli=1 + result.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); + result.setClickable(true); + result.setFocusable(false); + result.setLongClickable(true); +// result.setBackgroundResource(android.R.drawable.menuitem_background); + + result.setBackgroundResource(theme.normalRowBg); + } else if (result.getChildCount() > rowCount) { + result.removeViews(rowCount, result.getChildCount() - rowCount); + } - final TableRow.LayoutParams layoutParams = new TableRow.LayoutParams(); - layoutParams.weight = 0.5f; - layoutParams.leftMargin = mPaddingLarge; + for (int r = result.getChildCount(); r < rowCount; ++r) { + final TableRow.LayoutParams layoutParams = new TableRow.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT); + layoutParams.leftMargin = mPaddingLarge; - for (int r = 0; r < rowCount; ++r) { final TableRow tableRow = new TableRow(result.getContext()); final TextView col1 = new TextView(tableRow.getContext()); @@ -1579,51 +1601,12 @@ public class DictionaryActivity extends ActionBarActivity { col1.setTextIsSelectable(true); col2.setTextIsSelectable(true); } + col1.setTextColor(textColorFg); + col2.setTextColor(textColorFg); - // Set the columns in the table. - if (r > 0) { - final TextView bullet = new TextView(tableRow.getContext()); - bullet.setText(" • "); - tableRow.addView(bullet); - } - tableRow.addView(col1, layoutParams); - final TextView margin = new TextView(tableRow.getContext()); - margin.setText(" "); - tableRow.addView(margin); - if (r > 0) { - final TextView bullet = new TextView(tableRow.getContext()); - bullet.setText(" • "); - tableRow.addView(bullet); - } - tableRow.addView(col2, layoutParams); col1.setWidth(1); col2.setWidth(1); - // Set what's in the columns. - - final Pair pair = entry.pairs.get(r); - final String col1Text = index.swapPairEntries ? pair.lang2 : pair.lang1; - final String col2Text = index.swapPairEntries ? pair.lang1 : pair.lang2; - - col1.setText(col1Text, TextView.BufferType.SPANNABLE); - col2.setText(col2Text, TextView.BufferType.SPANNABLE); - - // Bold the token instances in col1. - final Set toBold = toHighlight != null ? this.toHighlight : Collections - .singleton(row.getTokenRow(true).getToken()); - final Spannable col1Spannable = (Spannable) col1.getText(); - for (final String token : toBold) { - int startPos = 0; - while ((startPos = col1Text.indexOf(token, startPos)) != -1) { - col1Spannable.setSpan(new StyleSpan(Typeface.BOLD), startPos, startPos - + token.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - startPos += token.length(); - } - } - - createTokenLinkSpans(col1, col1Spannable, col1Text); - createTokenLinkSpans(col2, (Spannable) col2.getText(), col2Text); - col1.setTypeface(typeface); col2.setTypeface(typeface); col1.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSizeSp); @@ -1638,18 +1621,59 @@ public class DictionaryActivity extends ActionBarActivity { col2.setOnLongClickListener(textViewLongClickListenerIndex1); } + // Set the columns in the table. + if (r == 0) { + tableRow.addView(col1, layoutParams); + tableRow.addView(col2, layoutParams); + } else { + for (int i = 0; i < 2; i++) { + final TextView bullet = new TextView(tableRow.getContext()); + bullet.setText(" • "); + LinearLayout wrapped = new LinearLayout(context); + wrapped.setOrientation(LinearLayout.HORIZONTAL); + LinearLayout.LayoutParams p1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, 0); + wrapped.addView(bullet, p1); + LinearLayout.LayoutParams p2 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT, 1); + wrapped.addView(i == 0 ? col1 : col2, p2); + tableRow.addView(wrapped, layoutParams); + } + } + result.addView(tableRow); } - // Because we have a Button inside a ListView row: - // http://groups.google.com/group/android-developers/browse_thread/thread/3d96af1530a7d62a?pli=1 - result.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); - result.setClickable(true); - result.setFocusable(false); - result.setLongClickable(true); -// result.setBackgroundResource(android.R.drawable.menuitem_background); + for (int r = 0; r < rowCount; ++r) { + final TableRow tableRow = (TableRow)result.getChildAt(r); + View left = tableRow.getChildAt(0); + View right = tableRow.getChildAt(1); + if (r > 0) { + left = ((ViewGroup)left).getChildAt(1); + right = ((ViewGroup)right).getChildAt(1); + } + final TextView col1 = (TextView)left; + final TextView col2 = (TextView)right; + + // Set what's in the columns. + final Pair pair = entry.pairs.get(r); + final String col1Text = index.swapPairEntries ? pair.lang2 : pair.lang1; + final String col2Text = index.swapPairEntries ? pair.lang1 : pair.lang2; + final Spannable col1Spannable = new SpannableString(col1Text); + final Spannable col2Spannable = new SpannableString(col2Text); + + // Bold the token instances in col1. + if (toHighlight != null) { + for (final String token : toHighlight) { + addBoldSpans(token, col1Text, col1Spannable); + } + } else + addBoldSpans(row.getTokenRow(true).getToken(), col1Text, col1Spannable); - result.setBackgroundResource(theme.normalRowBg); + createTokenLinkSpans(col1, col1Spannable, col1Text); + createTokenLinkSpans(col2, col2Spannable, col2Text); + + col1.setText(col1Spannable); + col2.setText(col2Spannable); + } result.setOnClickListener(new TextView.OnClickListener() { @Override @@ -1661,42 +1685,41 @@ public class DictionaryActivity extends ActionBarActivity { return result; } - private TableLayout getPossibleLinkToHtmlEntryView(final boolean isTokenRow, + private TextView getPossibleLinkToHtmlEntryView(final boolean isTokenRow, final String text, final boolean hasMainEntry, final List htmlEntries, - final String htmlTextToHighlight, ViewGroup parent, final TableLayout result) { + final String htmlTextToHighlight, ViewGroup parent, TextView textView) { final Context context = parent.getContext(); + if (textView == null) { + textView = new TextView(context); + // set up things invariant across one ItemViewType + // ItemViewTypes handled here are: + // 2: isTokenRow == true, htmlEntries.isEmpty() == true + // 3: isTokenRow == true, htmlEntries.isEmpty() == false + // 4: isTokenRow == false, htmlEntries.isEmpty() == false + textView.setPadding(isTokenRow ? mPaddingDefault : mPaddingLarge, mPaddingDefault, mPaddingDefault, 0); + textView.setOnLongClickListener(indexIndex > 0 ? textViewLongClickListenerIndex1 : textViewLongClickListenerIndex0); + textView.setLongClickable(true); + + textView.setTypeface(typeface); + if (isTokenRow) { + textView.setTextAppearance(context, theme.tokenRowFg); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 4 * fontSizeSp / 3); + } else { + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSizeSp); + } + textView.setTextColor(textColorFg); + if (!htmlEntries.isEmpty()) { + textView.setClickable(true); + textView.setMovementMethod(LinkMovementMethod.getInstance()); + } + } - final TableRow tableRow = new TableRow(result.getContext()); - tableRow.setBackgroundResource(hasMainEntry ? theme.tokenRowMainBg + textView.setBackgroundResource(hasMainEntry ? theme.tokenRowMainBg : theme.tokenRowOtherBg); - if (isTokenRow) { - tableRow.setPadding(mPaddingDefault, mPaddingDefault, mPaddingDefault, 0); - } else { - tableRow.setPadding(mPaddingLarge, mPaddingDefault, mPaddingDefault, 0); - } - result.addView(tableRow); // Make it so we can long-click on these token rows, too: - final TextView textView = new TextView(context); - 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.setTypeface(typeface); - 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); + final Spannable textSpannable = new SpannableString(text); + createTokenLinkSpans(textView, textSpannable, text); if (!htmlEntries.isEmpty()) { final ClickableSpan clickableSpan = new ClickableSpan() { @@ -1704,35 +1727,26 @@ public class DictionaryActivity extends ActionBarActivity { public void onClick(View widget) { } }; - ((Spannable) textView.getText()).setSpan(clickableSpan, 0, text.length(), + textSpannable.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) { showHtml(htmlEntries, htmlTextToHighlight); } }); - result.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - textView.performClick(); - } - }); } - result.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); - return result; + textView.setText(textSpannable); + return textView; } - private TableLayout getView(TokenRow row, ViewGroup parent, final TableLayout result) { + private TextView getView(TokenRow row, ViewGroup parent, final TextView 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) { + private TextView getView(HtmlEntry.Row row, ViewGroup parent, final TextView result) { final HtmlEntry htmlEntry = row.getEntry(); final TokenRow tokenRow = row.getTokenRow(true); return getPossibleLinkToHtmlEntryView(false, @@ -1743,7 +1757,7 @@ public class DictionaryActivity extends ActionBarActivity { } - static final Pattern CHAR_DASH = Pattern.compile("['\\p{L}\\p{M}\\p{N}]+"); + private static final Pattern CHAR_DASH = Pattern.compile("['\\p{L}\\p{M}\\p{N}]+"); private void createTokenLinkSpans(final TextView textView, final Spannable spannable, final String text) { @@ -1752,15 +1766,15 @@ public class DictionaryActivity extends ActionBarActivity { textView.setMovementMethod(LinkMovementMethod.getInstance()); final Matcher matcher = CHAR_DASH.matcher(text); while (matcher.find()) { - spannable.setSpan(new NonLinkClickableSpan(textColorFg), matcher.start(), + spannable.setSpan(new NonLinkClickableSpan(), matcher.start(), matcher.end(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); } } - String selectedSpannableText = null; + private String selectedSpannableText = null; - int selectedSpannableIndex = -1; + private int selectedSpannableIndex = -1; @Override public boolean onTouchEvent(MotionEvent event) { @@ -1789,22 +1803,22 @@ public class DictionaryActivity extends ActionBarActivity { } } - final TextViewLongClickListener textViewLongClickListenerIndex0 = new TextViewLongClickListener( + private final TextViewLongClickListener textViewLongClickListenerIndex0 = new TextViewLongClickListener( 0); - final TextViewLongClickListener textViewLongClickListenerIndex1 = new TextViewLongClickListener( + private final TextViewLongClickListener textViewLongClickListenerIndex1 = new TextViewLongClickListener( 1); // -------------------------------------------------------------------------- // SearchText // -------------------------------------------------------------------------- - void onSearchTextChange(final String text) { + private void onSearchTextChange(final String text) { if ("thadolina".equals(text)) { final Dialog dialog = new Dialog(getListView().getContext()); dialog.setContentView(R.layout.thadolina_dialog); dialog.setTitle("Ti amo, amore mio!"); - final ImageView imageView = (ImageView) dialog.findViewById(R.id.thadolina_image); + final ImageView imageView = dialog.findViewById(R.id.thadolina_image); imageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { @@ -1838,11 +1852,11 @@ public class DictionaryActivity extends ActionBarActivity { // Filtered results. // -------------------------------------------------------------------------- - boolean isFiltered() { + private boolean isFiltered() { return rowsToShow != null; } - void setFiltered(final SearchOperation searchOperation) { + private void setFiltered(final SearchOperation searchOperation) { if (nextWordMenuItem != null) { nextWordMenuItem.setEnabled(false); previousWordMenuItem.setEnabled(false); @@ -1851,7 +1865,7 @@ public class DictionaryActivity extends ActionBarActivity { setListAdapter(new IndexAdapter(index, rowsToShow, searchOperation.searchTokens)); } - void clearFiltered() { + private void clearFiltered() { if (nextWordMenuItem != null) { nextWordMenuItem.setEnabled(true); previousWordMenuItem.setEnabled(true);