diff --git a/res/drawable-hdpi/ic_find_next_mtrl_alpha.png b/res/drawable-hdpi/ic_find_next_mtrl_alpha.png new file mode 100644 index 00000000..6d5edac4 Binary files /dev/null and b/res/drawable-hdpi/ic_find_next_mtrl_alpha.png differ diff --git a/res/drawable-hdpi/ic_find_previous_mtrl_alpha.png b/res/drawable-hdpi/ic_find_previous_mtrl_alpha.png new file mode 100644 index 00000000..a5921afe Binary files /dev/null and b/res/drawable-hdpi/ic_find_previous_mtrl_alpha.png differ diff --git a/res/drawable-mdpi/ic_find_next_mtrl_alpha.png b/res/drawable-mdpi/ic_find_next_mtrl_alpha.png new file mode 100644 index 00000000..1cfdb3fe Binary files /dev/null and b/res/drawable-mdpi/ic_find_next_mtrl_alpha.png differ diff --git a/res/drawable-mdpi/ic_find_previous_mtrl_alpha.png b/res/drawable-mdpi/ic_find_previous_mtrl_alpha.png new file mode 100644 index 00000000..0d3c009d Binary files /dev/null and b/res/drawable-mdpi/ic_find_previous_mtrl_alpha.png differ diff --git a/res/drawable-xhdpi/ic_find_next_mtrl_alpha.png b/res/drawable-xhdpi/ic_find_next_mtrl_alpha.png new file mode 100644 index 00000000..9038282d Binary files /dev/null and b/res/drawable-xhdpi/ic_find_next_mtrl_alpha.png differ diff --git a/res/drawable-xhdpi/ic_find_previous_mtrl_alpha.png b/res/drawable-xhdpi/ic_find_previous_mtrl_alpha.png new file mode 100644 index 00000000..579347f2 Binary files /dev/null and b/res/drawable-xhdpi/ic_find_previous_mtrl_alpha.png differ diff --git a/res/drawable-xxhdpi/ic_find_next_mtrl_alpha.png b/res/drawable-xxhdpi/ic_find_next_mtrl_alpha.png new file mode 100644 index 00000000..e3a7e9e6 Binary files /dev/null and b/res/drawable-xxhdpi/ic_find_next_mtrl_alpha.png differ diff --git a/res/drawable-xxhdpi/ic_find_previous_mtrl_alpha.png b/res/drawable-xxhdpi/ic_find_previous_mtrl_alpha.png new file mode 100644 index 00000000..f9cf16cf Binary files /dev/null and b/res/drawable-xxhdpi/ic_find_previous_mtrl_alpha.png differ diff --git a/res/drawable/ic_find_next_material.xml b/res/drawable/ic_find_next_material.xml new file mode 100644 index 00000000..c6674ebf --- /dev/null +++ b/res/drawable/ic_find_next_material.xml @@ -0,0 +1,19 @@ + + + + diff --git a/res/drawable/ic_find_previous_material.xml b/res/drawable/ic_find_previous_material.xml new file mode 100644 index 00000000..32fcb315 --- /dev/null +++ b/res/drawable/ic_find_previous_material.xml @@ -0,0 +1,19 @@ + + + + diff --git a/res/layout/webview_find.xml b/res/layout/webview_find.xml new file mode 100644 index 00000000..5cdf67af --- /dev/null +++ b/res/layout/webview_find.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/res/menu/webview_find.xml b/res/menu/webview_find.xml new file mode 100644 index 00000000..41bb36e6 --- /dev/null +++ b/res/menu/webview_find.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 5ffdd9fc..01ba596d 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -96,4 +96,5 @@ Lade externen Inhalt Vollbild Lautstärketasten für die Navigation verwenden + Suche in Seite diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 324f8518..8684130e 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -102,5 +102,8 @@ Pantalla completa Usar botones de volumen para navegación Acceso de archivo denegado, saliendo... + Buscar en la página + Siguiente + Anterior" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 69a46428..cf08c998 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -102,5 +102,8 @@ Auto Plein écran Utiliser les boutons du volume +/- pour haut/bas + Rechercher sur la page + Suivant + Précédent diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 2b268ee8..9ef9a51c 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -94,7 +94,10 @@ is missing a quantity used in that language, otherwise the app will crash Stile interfaccia utente Chiaro Scuro - Carica contenuto remoto - Schermo intero - Usare i tasti del volume per la navigazione +Carica contenuto remoto +Schermo intero +Usare i tasti del volume per la navigazione +Trova nella pagina +Successivo +Precedente diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 86a30a11..cb0069d2 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -87,4 +87,5 @@ Загрузить удалённый контент Весь экран Использовать кнопки громкости для навигации + Поиск на странице diff --git a/res/values/strings.xml b/res/values/strings.xml index d8b041cf..83b24b17 100755 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -109,4 +109,7 @@ Full Screen Use volume buttons for navigation File access was not allowed, quitting... + Find in page + Next + Previous diff --git a/src/itkach/aard2/ArticleFragment.java b/src/itkach/aard2/ArticleFragment.java index 7341aeef..751e053d 100644 --- a/src/itkach/aard2/ArticleFragment.java +++ b/src/itkach/aard2/ArticleFragment.java @@ -73,7 +73,6 @@ private void displayBookmarked(boolean value) { } } - @SuppressWarnings("deprecation") @Override public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); diff --git a/src/itkach/aard2/ArticleWebView.java b/src/itkach/aard2/ArticleWebView.java index 378f801a..c47ad693 100644 --- a/src/itkach/aard2/ArticleWebView.java +++ b/src/itkach/aard2/ArticleWebView.java @@ -33,7 +33,7 @@ import java.util.TimerTask; import java.util.TreeSet; -public class ArticleWebView extends WebView { +public class ArticleWebView extends SearchableWebView { public static final String LOCALHOST = Application.LOCALHOST; private final String styleSwitcherJs; diff --git a/src/itkach/aard2/FindActionModeCallback.java b/src/itkach/aard2/FindActionModeCallback.java new file mode 100644 index 00000000..a962ae7f --- /dev/null +++ b/src/itkach/aard2/FindActionModeCallback.java @@ -0,0 +1,150 @@ +/* +* This file is heavily inspired by the Android Open Source Project +* licensed under the Apache License, Version 2.0 +*/ + +package itkach.aard2; + +import android.content.Context; +import android.os.Build; +import android.text.Editable; +import android.text.Selection; +import android.text.Spannable; +import android.text.TextWatcher; +import android.view.ActionMode; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +class FindActionModeCallback implements ActionMode.Callback, TextWatcher, + View.OnLongClickListener, View.OnClickListener { + + private View searchView; + private EditText editText; + private SearchableWebView webview; + private InputMethodManager imManager; + + FindActionModeCallback(Context context, SearchableWebView webview) { + this.webview = webview; + searchView = LayoutInflater.from(context).inflate(R.layout.webview_find, null); + + editText = searchView.findViewById(R.id.edit); + editText.setOnLongClickListener(this); + editText.setOnClickListener(this); + editText.addTextChangedListener(this); + + imManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + } + + /* Place text in the text field so it can be searched for. */ + void setText(String text) { + editText.setText(text); + Spannable span = (Spannable) editText.getText(); + int length = span.length(); + // Ideally, we would like to set the selection to the whole field, + // but this brings up the Text selection CAB, which dismisses this + // one. + Selection.setSelection(span, length, length); + // Necessary each time we set the text, so that this will watch + // changes to it. + span.setSpan(this, 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE); + } + + /* + * Move the highlight to the next match. + * @param next If true, find the next match further down in the document. + * If false, find the previous match, up in the document. + */ + private void findNext(boolean next) { + webview.findNext(next); + } + + /* + * Highlight all the instances of the string from editText in webview. + */ + void findAll() { + String find = editText.getText().toString(); + + if (Build.VERSION.SDK_INT < 16) + webview.findAll(find); + else + webview.findAllAsync(find); + } + + void showSoftInput() { + // imManager.showSoftInputMethod doesn't work + imManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + } + + // OnLongClickListener implementation + @Override + public boolean onLongClick(View v) { + // Override long click so that select ActionMode is not opened, which + // would exit find ActionMode. + return true; + } + + // OnClickListener implementation + @Override + public void onClick(View v) { + findNext(true); + } + + // ActionMode.Callback implementation + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + mode.setCustomView(searchView); + mode.getMenuInflater().inflate(R.menu.webview_find, menu); + + Editable edit = editText.getText(); + Selection.setSelection(edit, edit.length()); + editText.requestFocus(); + return true; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + webview.clearMatches(); + imManager.hideSoftInputFromWindow(webview.getWindowToken(), 0); + webview.setLastFind(editText.getText().toString()); + } + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + imManager.hideSoftInputFromWindow(webview.getWindowToken(), 0); + switch(item.getItemId()) { + case R.id.find_prev: + findNext(false); + break; + case R.id.find_next: + findNext(true); + break; + default: + return false; + } + return true; + } + + // TextWatcher implementation + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // Does nothing. Needed to implement TextWatcher. + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + findAll(); + } + + @Override + public void afterTextChanged(Editable s) { + // Does nothing. Needed to implement TextWatcher. + } +} \ No newline at end of file diff --git a/src/itkach/aard2/SearchableWebView.java b/src/itkach/aard2/SearchableWebView.java new file mode 100644 index 00000000..f112a271 --- /dev/null +++ b/src/itkach/aard2/SearchableWebView.java @@ -0,0 +1,63 @@ +/* + * This file is heavily inspired by the Android Open Source Project + * licensed under the Apache License, Version 2.0 + */ + +package itkach.aard2; + +import android.content.Context; +import android.util.AttributeSet; +import android.webkit.WebView; + +class SearchableWebView extends WebView { + + private String mLastFind = null; + + public void setLastFind(String find) { + mLastFind = find; + } + + public SearchableWebView(Context context) { + this(context, null); + } + + public SearchableWebView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + /** + * Start an ActionMode for finding text in this WebView. Only works if this + * WebView is attached to the view system. + * + * @param text If non-null, will be the initial text to search for. + * Otherwise, the last String searched for in this WebView will + * be used to start. + * @param showIme If true, show the IME, assuming the user will begin typing. + * If false and text is non-null, perform a find all. + * @return boolean True if the find dialog is shown, false otherwise. + */ + @Override + public boolean showFindDialog(String text, boolean showIme) { + FindActionModeCallback callback = new FindActionModeCallback(getContext(), this); + if (getParent() == null || startActionMode(callback) == null) { + // Could not start the action mode, so end Find on page + return false; + } + + if (showIme) { + callback.showSoftInput(); + } else if (text != null) { + callback.setText(text); + callback.findAll(); + return true; + } + if (text == null) { + text = mLastFind; + } + if (text != null) { + callback.setText(text); + callback.findAll(); + } + return true; + } +}