Implement find on page. https://redmine.stoutner.com/issues/9
authorSoren Stoutner <soren@stoutner.com>
Sat, 5 Nov 2016 17:35:42 +0000 (10:35 -0700)
committerSoren Stoutner <soren@stoutner.com>
Sat, 5 Nov 2016 17:35:42 +0000 (10:35 -0700)
.idea/dictionaries/soren.xml
app/src/free/res/layout/main_webview.xml
app/src/main/java/com/stoutner/privacybrowser/MainWebViewActivity.java
app/src/main/res/layout/find_on_page_app_bar.xml
app/src/main/res/layout/main_webview.xml
app/src/main/res/values/strings.xml

index fa88d99..2e3bd78 100644 (file)
       <w>samsung</w>
       <w>securitypatch</w>
       <w>sendto</w>
+      <w>showsoftinput</w>
       <w>snackbar</w>
       <w>snackbars</w>
+      <w>softkeyboard</w>
       <w>subfolders</w>
       <w>tablayout</w>
       <w>techrepublic</w>
index 17d0206..403bb64 100644 (file)
@@ -21,7 +21,7 @@
 <!-- android:layout_martinTop="?attr/actionBarSize" moves adRelativeLayout below the appBarLayout, which otherwise would cover the top of mainWebView.
   android:layout_weight="1" sets the RelativeLayout to fill the rest of the screen because it is encapsulated in a LinearLayout with android:orientation="vertical"-->
 <RelativeLayout
-    android:id="@+id/adRelativeLayout"
+    android:id="@+id/mainWebViewRelativeLayout"
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     xmlns:ads="http://schemas.android.com/apk/res-auto"
index 1553114..7928c94 100644 (file)
@@ -50,6 +50,8 @@ import android.support.v7.app.ActionBarDrawerToggle;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.app.AppCompatDialogFragment;
 import android.support.v7.widget.Toolbar;
+import android.text.Editable;
+import android.text.TextWatcher;
 import android.util.Patterns;
 import android.view.KeyEvent;
 import android.view.Menu;
@@ -69,6 +71,8 @@ import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
 
 import java.io.UnsupportedEncodingException;
 import java.net.MalformedURLException;
@@ -97,7 +101,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
     public static SslCertificate sslCertificate;
 
 
-    // 'mainWebView' is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, and `loadUrlFromTextBox()`.
+    // 'mainWebView' is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `findPreviousOnPage()`, `findNextOnPage()`, `closeFindOnPage`, and `loadUrlFromTextBox()`.
     private WebView mainWebView;
 
     // `swipeRefreshLayout` is used in `onCreate()`, `onPrepareOptionsMenu`, and `onRestart()`.
@@ -205,29 +209,67 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
             }
         });
 
-        // Get a handle for `find_on_page_edittext`.
+        // Get handles for `fullScreenVideoFrameLayout`, `mainWebView`, and `find_on_page_edittext`.
+        final FrameLayout fullScreenVideoFrameLayout = (FrameLayout) findViewById(R.id.fullScreenVideoFrameLayout);
+        mainWebView = (WebView) findViewById(R.id.mainWebView);
         findOnPageEditText = (EditText) findViewById(R.id.find_on_page_edittext);
 
-        // Set the `go` button on the keyboard to search for the phrase in `find_on_page_edittext`
+        // Update `findOnPageCountTextView`.
+        mainWebView.setFindListener(new WebView.FindListener() {
+            // Get a handle for `findOnPageCountTextView`.
+            TextView findOnPageCountTextView = (TextView) findViewById(R.id.find_on_page_count_textview);
+
+            @Override
+            public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, boolean isDoneCounting) {
+                if ((isDoneCounting) && (numberOfMatches == 0)) {  // There are no matches.
+                    // Set `findOnPageCountTextView` to `0/0`.
+                    findOnPageCountTextView.setText(R.string.zero_of_zero);
+                } else if (isDoneCounting) {  // There are matches.
+                    // `activeMatchOrdinal` is zero-based.
+                    int activeMatch = activeMatchOrdinal + 1;
+
+                    // Set `findOnPageCountTextView`.
+                    findOnPageCountTextView.setText(activeMatch + "/" + numberOfMatches);
+                }
+            }
+        });
+
+        // Search for the string on the page whenever a character changes in the `findOnPageEditText`.
+        findOnPageEditText.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+                // Do nothing.
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+                // Do nothing.
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                // Search for the text in `mainWebView`.
+                mainWebView.findAllAsync(findOnPageEditText.getText().toString());
+            }
+        });
+
+        // Set the `check mark` button for the `findOnPageEditText` keyboard to close the soft keyboard.
         findOnPageEditText.setOnKeyListener(new View.OnKeyListener() {
             @Override
             public boolean onKey(View v, int keyCode, KeyEvent event) {
-                // If the event is a key-down event on the `enter` button, search for the phrase.
-                if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
-                    // Search for the phrase.
-                    mainWebView.findAllAsync(findOnPageEditText.getText().toString());
+                if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {  // The `enter` key was pressed.
+                    // Hide the soft keyboard.  `0` indicates no additional flags.
+                    inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
 
                     // Consume the event.
                     return true;
-                } else {
+                } else {  // A different key was pressed.
                     // Do not consume the event.
                     return false;
                 }
             }
         });
 
-        final FrameLayout fullScreenVideoFrameLayout = (FrameLayout) findViewById(R.id.fullScreenVideoFrameLayout);
-
         // Implement swipe to refresh
         swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
         swipeRefreshLayout.setColorSchemeResources(R.color.blue_700);
@@ -238,8 +280,6 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
             }
         });
 
-        mainWebView = (WebView) findViewById(R.id.mainWebView);
-
         // Create the navigation drawer.
         drawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
         // The DrawerTitle identifies the drawer in accessibility mode.
@@ -780,6 +820,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 findOnPageLinearLayout.setVisibility(View.VISIBLE);
 
                 // Display the keyboard.  We have to wait 200 ms before running the command to work around a bug in Android.
+                // http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working
                 findOnPageEditText.postDelayed(new Runnable()
                 {
                     @Override
@@ -919,7 +960,11 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 // Clear `customHeaders`.
                 customHeaders.clear();
 
-                // Destroy the internal state of the webview.
+                // Detach all views from `mainWebViewRelativeLayout`.
+                RelativeLayout mainWebViewRelativeLayout = (RelativeLayout) findViewById(R.id.mainWebViewRelativeLayout);
+                mainWebViewRelativeLayout.removeAllViews();
+
+                // Destroy the internal state of `mainWebView`.
                 mainWebView.destroy();
 
                 // Close Privacy Browser.  `finishAndRemoveTask` also removes Privacy Browser from the recent app list.
@@ -1123,10 +1168,23 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
     }
 
+    public void findPreviousOnPage(View view) {
+        // Go to the previous highlighted phrase on the page.  `false` goes backwards instead of forwards.
+        mainWebView.findNext(false);
+    }
+
+    public void findNextOnPage(View view) {
+        // Go to the next highlighted phrase on the page. `true` goes forwards instead of backwards.
+        mainWebView.findNext(true);
+    }
+
     public void closeFindOnPage(View view) {
         // Delete the contents of `find_on_page_edittext`.
         findOnPageEditText.setText(null);
 
+        // Clear the highlighted phrases.
+        mainWebView.clearMatches();
+
         // Hide the Find on Page `RelativeLayout`.
         LinearLayout findOnPageLinearLayout = (LinearLayout) findViewById(R.id.find_on_page_linearlayout);
         findOnPageLinearLayout.setVisibility(View.GONE);
index 56f0c1e..ea8cdd8 100644 (file)
@@ -27,7 +27,7 @@
     android:orientation="horizontal"
     android:visibility="gone" >
 
-    <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key. -->
+    <!-- `android:imeOptions="actionDone"` sets the keyboard to have a `check mark` key instead of a `new line` key. -->
     <EditText
         android:id="@+id/find_on_page_edittext"
         android:layout_height="wrap_content"
         android:layout_marginEnd="4dp"
         android:hint="@string/find_on_page"
         android:lines="1"
-        android:imeOptions="actionGo"
+        android:imeOptions="actionDone"
         android:inputType="text" />
 
+    <TextView
+        android:id="@+id/find_on_page_count_textview"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_marginStart="4dp"
+        android:layout_marginEnd="4dp"
+        android:text="@string/zero_of_zero"
+        />
+
     <ImageView
         android:id="@+id/find_previous"
         android:src="@drawable/previous"
@@ -48,7 +57,8 @@
         android:layout_marginStart="4dp"
         android:layout_marginEnd="4dp"
         android:layout_gravity="center_vertical"
-        android:contentDescription="@string/previous" />
+        android:contentDescription="@string/previous"
+        android:onClick="findPreviousOnPage"/>
 
     <ImageView
         android:id="@+id/find_next"
@@ -58,7 +68,8 @@
         android:layout_marginStart="4dp"
         android:layout_marginEnd="4dp"
         android:layout_gravity="center_vertical"
-        android:contentDescription="@string/next" />
+        android:contentDescription="@string/next"
+        android:onClick="findNextOnPage"/>
 
     <ImageView
         android:id="@+id/close_find"
index b792066..17597d6 100644 (file)
   along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>. -->
 
 <!-- android:layout_martinTop="?attr/actionBarSize" moves the RelativeLayout below the appBarLayout, which otherwise would cover the top of mainWebView.
-  the RelativeLayout has an id of adView in the standard flavor so that the ad commands (which do nothing in the standard flavor) don't produce errors.
   android:layout_weight="1" sets the RelativeLayout to fill the rest of the screen because it is encapsulated in a LinearLayout with android:orientation="vertical". -->
 <RelativeLayout
-    android:id="@+id/adView"
+    android:id="@+id/mainWebViewRelativeLayout"
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     tools:context="com.stoutner.privacybrowser.MainWebViewActivity"
     tools:showIn="@layout/main_coordinatorlayout" >
 
+    <!-- This `TextView` has an id of `adView` so that the ad commands (which do nothing in the standard flavor) don't produce errors. -->
+    <TextView
+        android:id="@+id/adView"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:visibility="gone" />
+
     <android.support.v4.widget.SwipeRefreshLayout
         android:id="@+id/swipeRefreshLayout"
         android:layout_width="match_parent"
         android:layout_height="match_parent" >
 
-        <!-- Google does not currently want to support hiding the AppBar on scroll for a WebView child with the Support Toolbar.
-          https://code.google.com/p/android/issues/detail?id=200394 -->
+        <!-- Google does not currently want to support hiding the AppBar on scroll for a WebView child with the Support Toolbar.  https://code.google.com/p/android/issues/detail?id=200394 -->
         <WebView
             android:id="@+id/mainWebView"
             android:layout_width="match_parent"
index 1a540fc..1d997bc 100644 (file)
     <string name="refresh">Refresh</string>
 
     <!-- Find on Page. -->
+    <string name="zero_of_zero" translatable="false">0/0</string>
     <string name="previous">Previous</string>
     <string name="next">Next</string>