import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.MatrixCursor;
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.support.v7.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.v4.widget.CursorAdapter;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AutoCompleteTextView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.FrameLayout;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
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;
});
private SearchOperation currentSearchOperation = null;
+ private final int MAX_SEARCH_HISTORY = 100;
+ private final int DEFAULT_SEARCH_HISTORY = 10;
+ private int searchHistoryLimit;
+ private final ArrayList<String> searchHistory = new ArrayList<>(DEFAULT_SEARCH_HISTORY);
+ private MatrixCursor searchHistoryCursor = new MatrixCursor(new String[] {"_id", "search"});
private TextToSpeech textToSpeech;
private volatile boolean ttsReady;
}
private SearchView searchView;
+ private AutoCompleteTextView searchTextView;
private ImageButton languageButton;
private SearchView.OnQueryTextListener onQueryTextListener;
Log.d(LOG, "onSaveInstanceState: " + searchView.getQuery().toString());
outState.putString(C.INDEX_SHORT_NAME, index.shortName);
outState.putString(C.SEARCH_TOKEN, searchView.getQuery().toString());
+ outState.putStringArrayList(C.SEARCH_HISTORY, searchHistory);
}
private int getMatchLen(String search, Index.IndexEntry e) {
finish();
}
+ private void saveSearchHistory() {
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ final SharedPreferences.Editor ed = prefs.edit();
+ for (int i = 0; i < searchHistory.size(); i++) {
+ ed.putString("history" + i, searchHistory.get(i));
+ }
+ for (int i = searchHistory.size(); i <= MAX_SEARCH_HISTORY; i++) {
+ ed.remove("history" + i);
+ }
+ ed.apply();
+ }
+
+ private void addToSearchHistory() {
+ addToSearchHistory(searchView.getQuery().toString());
+ }
+
+ private void addToSearchHistory(String text) {
+ if (text == null || text.isEmpty() || searchHistoryLimit == 0) return;
+ int exists = searchHistory.indexOf(text);
+ if (exists >= 0) searchHistory.remove(exists);
+ else if (searchHistory.size() >= searchHistoryLimit) searchHistory.remove(searchHistory.size() - 1);
+ searchHistory.add(0, text);
+ searchHistoryCursor = new MatrixCursor(new String[] {"_id", "search"});
+ for (int i = 0; i < searchHistory.size(); i++) {
+ final Object[] row = {i, searchHistory.get(i)};
+ searchHistoryCursor.addRow(row);
+ }
+ if (searchView.getSuggestionsAdapter().getCursor() != null) {
+ searchView.getSuggestionsAdapter().swapCursor(searchHistoryCursor);
+ searchView.getSuggestionsAdapter().notifyDataSetChanged();
+ }
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
DictionaryApplication.INSTANCE.init(getApplicationContext());
* -> 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")) {
+ if ("com.hughes.action.ACTION_SEARCH_DICT".equals(intentAction)) {
String query = intent.getStringExtra(SearchManager.QUERY);
String from = intent.getStringExtra("from");
if (from != null)
fontSizeSp = 14;
}
+ final String searchHistoryLimitStr = prefs.getString(getString(R.string.historySizeKey), "" + DEFAULT_SEARCH_HISTORY);
+ try {
+ searchHistoryLimit = Math.min(Integer.parseInt(searchHistoryLimitStr.trim()), MAX_SEARCH_HISTORY);
+ } catch (NumberFormatException e) {
+ searchHistoryLimit = DEFAULT_SEARCH_HISTORY;
+ }
+
// ContextMenu.
registerForContextMenu(getListView());
if (text == null) {
text = "";
}
+
+ searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
+ @Override
+ public boolean onSuggestionSelect(int position) {
+ return false;
+ }
+
+ @Override
+ public boolean onSuggestionClick(int position) {
+ String h = searchHistory.get(position);
+ addToSearchHistory(h);
+ setSearchText(h, true);
+ return true;
+ }
+ });
+ searchView.setSuggestionsAdapter(new CursorAdapter(this, text.isEmpty() ? searchHistoryCursor : null, 0) {
+ @Override
+ public View newView(Context context, Cursor c, ViewGroup p) {
+ TextView v = new TextView(context);
+ v.setTextColor(textColorFg);
+ v.setTypeface(typeface);
+ v.setTextSize(TypedValue.COMPLEX_UNIT_SP, 4 * fontSizeSp / 3);
+ return v;
+ }
+ @Override
+ public void bindView(View v, Context context, Cursor c) {
+ TextView t = (TextView)v;
+ t.setText(c.getString(1));
+ }
+ });
+
+ // Set up search history
+ ArrayList<String> savedHistory = null;
+ if (savedInstanceState != null) savedHistory = savedInstanceState.getStringArrayList(C.SEARCH_HISTORY);
+ if (savedHistory != null && !savedHistory.isEmpty()) {
+ } else {
+ savedHistory = new ArrayList<>();
+ for (int i = 0; i < searchHistoryLimit; i++) {
+ String h = prefs.getString("history" + i, null);
+ if (h == null) break;
+ savedHistory.add(h);
+ }
+ }
+ for (int i = savedHistory.size() - 1; i >= 0; i--) {
+ addToSearchHistory(savedHistory.get(i));
+ }
+ addToSearchHistory(text);
+
setSearchText(text, true);
Log.d(LOG, "Trying to restore searchText=" + text);
@Override
public boolean onQueryTextSubmit(String query) {
Log.d(LOG, "OnQueryTextListener: onQueryTextSubmit: " + searchView.getQuery());
+ addToSearchHistory();
hideKeyboard();
return true;
}
};
searchView.setOnQueryTextListener(onQueryTextListener);
searchView.setFocusable(true);
+ searchTextView = searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0,
FrameLayout.LayoutParams.WRAP_CONTENT, 1);
customSearchView.addView(searchView, lp);
prefs.commit();
}
+ @Override
+ protected void onPause() {
+ super.onPause();
+ addToSearchHistory();
+ saveSearchHistory();
+ }
+
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(LOG, "Trying to show soft keyboard.");
final boolean searchTextHadFocus = searchView.hasFocus();
searchView.requestFocusFromTouch();
+ searchTextView.requestFocus();
final InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
manager.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT);
+ manager.showSoftInput(searchTextView, InputMethodManager.SHOW_IMPLICIT);
if (!searchTextHadFocus) {
defocusSearchText();
}
}
}, delay);
}
+ searchView.post(new Runnable() {
+ @Override
+ public void run() {
+ searchTextView.setThreshold(0);
+ try {
+ searchTextView.showDropDown();
+ // ignore any errors, in particular BadTokenException happens a lot
+ } catch (Exception e) {}
+ }
+ });
}
private void hideKeyboard() {
updateTTSLanguage(indexIndex);
}
+ @SuppressWarnings("deprecation")
+ private void speak(String text) {
+ textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, null);
+ }
+
private void updateTTSLanguage(int i) {
if (!ttsReady || index == null || textToSpeech == null) {
Log.d(LOG, "Can't updateTTSLanguage.");
searchView.requestFocus();
}
if (searchView.getQuery().toString().length() > 0) {
+ addToSearchHistory();
searchView.setQuery("", false);
}
showKeyboard();
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(),
indexInfo, application.languageButtonPixels);
speak.setOnMenuItemClickListener(new android.view.MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(android.view.MenuItem item) {
- textToSpeech.speak(textToSpeak, TextToSpeech.QUEUE_FLUSH,
- new HashMap<String, String>());
+ speak(textToSpeak);
return false;
}
});
String text = "";
for (Pair p : pairs) text += p.get(idx);
text = text.replaceAll("\\{[^{}]*\\}", "").replace("{", "").replace("}", "");
- textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH,
- new HashMap<String, String>());
+ speak(text);
return false;
}
});
String text = "";
for (Pair p : pairs) text += p.get(idx);
text = text.replaceAll("\\{[^{}]*\\}", "").replace("{", "").replace("}", "");
- textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH,
- new HashMap<String, String>());
+ speak(text);
return false;
}
});
@Override
public void run() {
setIndexAndSearchText(actualIndexToUse, selectedText, true);
+ addToSearchHistory(selectedText);
}
}, 100);
}
try {
wordList.getParentFile().mkdirs();
final PrintWriter out = new PrintWriter(new FileWriter(wordList, true));
- out.println(rawText.toString());
+ out.println(rawText);
out.close();
} catch (Exception e) {
Log.e(LOG, "Unable to append to " + wordList.getAbsolutePath(), e);
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);
}
Log.d(LOG, "searchFinished: " + searchOperation + ", searchResult=" + searchResult);
currentSearchOperation = null;
- uiHandler.postDelayed(new Runnable() {
+ // Note: it's important to post to the ListView, otherwise
+ // the jumpToRow will randomly not work.
+ getListView().post(new Runnable() {
@Override
public void run() {
if (currentSearchOperation == null) {
Log.d(LOG, "More coming, waiting for currentSearchOperation.");
}
}
- }, 20);
+ });
}
- private final void jumpToRow(final int row) {
+ private void jumpToRow(final int row) {
Log.d(LOG, "jumpToRow: " + row + ", refocusSearchText=" + false);
// getListView().requestFocusFromTouch();
getListView().setSelectionFromTop(row, 0);
}
public String toString() {
- return String.format("SearchOperation(%s,%s)", searchText, interrupted.toString());
+ return String.format("SearchOperation(%s,%s)", searchText, interrupted);
}
@Override
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;
private void showHtml(final List<HtmlEntry> htmlEntries, final String htmlTextToHighlight) {
String html = HtmlEntry.htmlBody(htmlEntries, index.shortName);
+ String style = "";
+ if (typeface == Typeface.SERIF) { style = "font-family: serif;"; }
+ else if (typeface == Typeface.SANS_SERIF) { style = "font-family: sans-serif;"; }
+ else if (typeface == Typeface.MONOSPACE) { style = "font-family: monospace;"; }
+ if (application.getSelectedTheme() == DictionaryApplication.Theme.DEFAULT)
+ style += "body { background-color: black; color: white; } a { color: #00aaff; }";
// Log.d(LOG, "html=" + html);
startActivityForResult(
HtmlDisplayActivity.getHtmlIntent(getApplicationContext(), String.format(
- "<html><head><meta name=\"viewport\" content=\"width=device-width\"></head><body>%s</body></html>", html),
+ "<html><head><meta name=\"viewport\" content=\"width=device-width\"><style type=\"text/css\">%s</style></head><body>%s</body></html>", style, html),
htmlTextToHighlight, false),
0);
}
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);
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);
- 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);
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);
}
for (int r = 0; r < rowCount; ++r) {
final TableRow tableRow = (TableRow)result.getChildAt(r);
- final TextView col1 = (TextView)tableRow.getChildAt(r == 0 ? 0 : 1);
- final TextView col2 = (TextView)tableRow.getChildAt(r == 0 ? 1 : 3);
+ 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);
currentSearchOperation = new SearchOperation(text, index);
searchExecutor.execute(currentSearchOperation);
((FloatingActionButton)findViewById(R.id.floatSearchButton)).setImageResource(text.length() > 0 ? R.drawable.ic_clear_black_24dp : R.drawable.ic_search_black_24dp);
+ searchView.getSuggestionsAdapter().swapCursor(text.isEmpty() ? searchHistoryCursor : null);
+ searchView.getSuggestionsAdapter().notifyDataSetChanged();
}
// --------------------------------------------------------------------------