]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/MainWebViewActivity.java
Make basic progress with the `X-Requested-With` header.
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / MainWebViewActivity.java
index c7dddad31434d1ee4fb07b155b49c68946f1fbf8..f27f961580a772867b7cbb0f1fe750cc52ee23b6 100644 (file)
@@ -21,25 +21,27 @@ package com.stoutner.privacybrowser;
 
 import android.annotation.SuppressLint;
 import android.app.Activity;
+import android.app.DialogFragment;
 import android.app.DownloadManager;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
 import android.support.design.widget.NavigationView;
 import android.support.design.widget.Snackbar;
-import android.support.v4.app.DialogFragment;
+import android.support.v4.content.ContextCompat;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v4.widget.SwipeRefreshLayout;
 import android.support.v7.app.ActionBar;
 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.util.Patterns;
 import android.view.KeyEvent;
@@ -53,6 +55,7 @@ import android.webkit.WebChromeClient;
 import android.webkit.WebStorage;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
+import android.webkit.WebViewDatabase;
 import android.widget.EditText;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -62,48 +65,79 @@ import java.io.UnsupportedEncodingException;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
 
 // We need to use AppCompatActivity from android.support.v7.app.AppCompatActivity to have access to the SupportActionBar until the minimum API is >= 21.
 public class MainWebViewActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, CreateHomeScreenShortcut.CreateHomeScreenSchortcutListener {
-    // favoriteIcon is public static so it can be accessed from CreateHomeScreenShortcut.
+    // `favoriteIcon` is public static so it can be accessed from `CreateHomeScreenShortcut`, `BookmarksActivity`, `CreateBookmark`, `CreateBookmarkFolder`, and `EditBookmark`.
+    // It is also used in `onCreate()` and `onCreateHomeScreenShortcutCreate()`.
     public static Bitmap favoriteIcon;
-    // mainWebView is public static so it can be accessed from SettingsFragment.  It is also used in onCreate(), onOptionsItemSelected(), onNavigationItemSelected(), and loadUrlFromTextBox().
+
+    // `mainWebView` is public static so it can be accessed from `SettingsFragment`.
+    // It is also used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, and `loadUrlFromTextBox()`.
     public static WebView mainWebView;
 
-    // mainMenu is public static so it can be accessed from SettingsFragment.  It is also used in onCreateOptionsMenu() and onOptionsItemSelected().
+    // `formattedUrlString` is public static so it can be accessed from `BookmarksActivity`.
+    // It is also used in `onCreate()`, `onOptionsItemSelected()`, `onCreateHomeScreenShortcutCreate()`, and `loadUrlFromTextBox()`.
+    public static String formattedUrlString;
+
+    // `mainMenu` is public static so it can be accessed from `SettingsFragment`.  It is also used in `onCreateOptionsMenu()` and `onOptionsItemSelected()`.
     public static Menu mainMenu;
-    // cookieManager is public static so it can be accessed from SettingsFragment.  It is also used in onCreate(), onOptionsItemSelected(), and onNavigationItemSelected().
+
+    // `cookieManager` is public static so it can be accessed from `SettingsFragment`.  It is also used in `onCreate()`, `onOptionsItemSelected()`, and `onNavigationItemSelected()`.
     public static CookieManager cookieManager;
-    // javaScriptEnabled is public static so it can be accessed from SettingsFragment.  It is also used in onCreate(), onCreateOptionsMenu(), onOptionsItemSelected(), and loadUrlFromTextBox().
+
+    // `javaScriptEnabled` is public static so it can be accessed from `SettingsFragment`.
+    // It is also used in `onCreate()`, `onCreateOptionsMenu()`, `onOptionsItemSelected()`, and `loadUrlFromTextBox()`.
     public static boolean javaScriptEnabled;
-    // firstPartyCookiesEnabled is public static so it can be accessed from SettingsFragment.  It is also used in onCreate(), onCreateOptionsMenu(), onPrepareOptionsMenu(), and onOptionsItemSelected().
+
+    // `firstPartyCookiesEnabled` is public static so it can be accessed from `SettingsFragment`.
+    // It is also used in `onCreate()`, `onCreateOptionsMenu()`, `onPrepareOptionsMenu()`, and `onOptionsItemSelected()`.
     public static boolean firstPartyCookiesEnabled;
-    // thirdPartyCookiesEnabled is used in onCreate(), onCreateOptionsMenu(), onPrepareOptionsMenu(), and onOptionsItemSelected().
+
+    // `thirdPartyCookiesEnabled` is used in `onCreate()`, `onCreateOptionsMenu()`, `onPrepareOptionsMenu()`, and `onOptionsItemSelected()`.
     public static boolean thirdPartyCookiesEnabled;
-    // domStorageEnabled is public static so it can be accessed from SettingsFragment.  It is also used in onCreate(), onCreateOptionsMenu(), and onOptionsItemSelected().
+
+    // `domStorageEnabled` is public static so it can be accessed from `SettingsFragment`.  It is also used in `onCreate()`, `onCreateOptionsMenu()`, and `onOptionsItemSelected()`.
     public static boolean domStorageEnabled;
-    // javaScriptDisabledSearchURL is public static so it can be accessed from SettingsFragment.  It is also used in onCreate() and loadURLFromTextBox().
+
+    // `saveFormDataEnabled` is public static so it can be accessed from `SettingsFragment`.  It is also used in `onCreate()`, `onCreateOptionsMenu()`, and `onOptionsItemSelected()`.
+    public static boolean saveFormDataEnabled;
+
+    // `javaScriptDisabledSearchURL` is public static so it can be accessed from `SettingsFragment`.  It is also used in `onCreate()` and `loadURLFromTextBox()`.
     public static String javaScriptDisabledSearchURL;
-    // javaScriptEnabledSearchURL is public static so it can be accessed from SettingsFragment.  It is also used in onCreate() and loadURLFromTextBox().
+
+    // `javaScriptEnabledSearchURL` is public static so it can be accessed from `SettingsFragment`.  It is also used in `onCreate()` and `loadURLFromTextBox()`.
     public static String javaScriptEnabledSearchURL;
-    // homepage is public static so it can be accessed from  SettingsFragment.  It is also used in onCreate() and onOptionsItemSelected().
+
+    // `homepage` is public static so it can be accessed from `SettingsFragment`.  It is also used in `onCreate()` and `onOptionsItemSelected()`.
     public static String homepage;
-    // swipeToRefresh is public static so it can be accessed from SettingsFragment.  It is also used in onCreate().
+
+    // `swipeToRefresh` is public static so it can be accessed from SettingsFragment.  It is also used in onCreate().
     public static SwipeRefreshLayout swipeToRefresh;
-    // swipeToRefreshEnabled is public static so it can be accessed from SettingsFragment.  It is also used in onCreate().
+
+    // `swipeToRefreshEnabled` is public static so it can be accessed from `SettingsFragment`.  It is also used in `onCreate()`.
     public static boolean swipeToRefreshEnabled;
 
-    // drawerToggle is used in onCreate(), onPostCreate(), onConfigurationChanged(), onNewIntent(), and onNavigationItemSelected().
+    // `customHeader` is public static so it can be accessed from `BookmarksActivity`.  It is also used in `onCreate()` and `loadUrlFromTextBox()`.
+    public static Map<String, String> customHeaders = new HashMap<String, String>();
+
+
+
+    // `drawerToggle` is used in `onCreate()`, `onPostCreate()`, `onConfigurationChanged()`, `onNewIntent()`, and `onNavigationItemSelected()`.
     private ActionBarDrawerToggle drawerToggle;
-    // drawerLayout is used in onCreate(), onNewIntent(), and onBackPressed().
+
+    // `drawerLayout` is used in `onCreate()`, `onNewIntent()`, and `onBackPressed()`.
     private DrawerLayout drawerLayout;
-    // formattedUrlString is used in onCreate(), onOptionsItemSelected(), onCreateHomeScreenShortcutCreate(), and loadUrlFromTextBox().
-    private String formattedUrlString;
-    // privacyIcon is used in onCreateOptionsMenu() and updatePrivacyIcon().
+
+    // `privacyIcon` is used in `onCreateOptionsMenu()` and `updatePrivacyIcon()`.
     private MenuItem privacyIcon;
-    // urlTextBox is used in onCreate(), onOptionsItemSelected(), and loadUrlFromTextBox().
+
+    // `urlTextBox` is used in `onCreate()`, `onOptionsItemSelected()`, and `loadUrlFromTextBox()`.
     private EditText urlTextBox;
-    // adView is used in onCreate() and onConfigurationChanged().
+
+    // `adView` is used in `onCreate()` and `onConfigurationChanged()`.
     private View adView;
 
     @Override
@@ -111,16 +145,42 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
     @SuppressLint("SetJavaScriptEnabled")
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.coordinator_layout);
+        setContentView(R.layout.main_coordinatorlayout);
 
         // We need to use the SupportActionBar from android.support.v7.app.ActionBar until the minimum API is >= 21.
         Toolbar supportAppBar = (Toolbar) findViewById(R.id.appBar);
         setSupportActionBar(supportAppBar);
+        final ActionBar appBar = getSupportActionBar();
 
-        final FrameLayout fullScreenVideoFrameLayout = (FrameLayout) findViewById(R.id.fullScreenVideoFrameLayout);
+        // This is needed to get rid of the Android Studio warning that appBar might be null.
+        assert appBar != null;
+
+        // Add the custom url_bar layout, which shows the favoriteIcon, urlTextBar, and progressBar.
+        appBar.setCustomView(R.layout.url_bar);
+        appBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
+
+        // Set the "go" button on the keyboard to load the URL in urlTextBox.
+        urlTextBox = (EditText) appBar.getCustomView().findViewById(R.id.urlTextBox);
+        urlTextBox.setOnKeyListener(new View.OnKeyListener() {
+            public boolean onKey(View v, int keyCode, KeyEvent event) {
+                // If the event is a key-down event on the "enter" button, load the URL.
+                if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
+                    // Load the URL into the mainWebView and consume the event.
+                    try {
+                        loadUrlFromTextBox();
+                    } catch (UnsupportedEncodingException e) {
+                        e.printStackTrace();
+                    }
+                    // If the enter key was pressed, consume the event.
+                    return true;
+                } else {
+                    // If any other key was pressed, do not consume the event.
+                    return false;
+                }
+            }
+        });
 
-        // We need to use the SupportActionBar from android.support.v7.app.ActionBar until the minimum API is >= 21.
-        final ActionBar appBar = getSupportActionBar();
+        final FrameLayout fullScreenVideoFrameLayout = (FrameLayout) findViewById(R.id.fullScreenVideoFrameLayout);
 
         // Implement swipe to refresh
         swipeToRefresh = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
@@ -135,33 +195,6 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
 
         mainWebView = (WebView) findViewById(R.id.mainWebView);
 
-        if (appBar != null) {
-            // Add the custom url_bar layout, which shows the favoriteIcon, urlTextBar, and progressBar.
-            appBar.setCustomView(R.layout.url_bar);
-            appBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
-
-            // Set the "go" button on the keyboard to load the URL in urlTextBox.
-            urlTextBox = (EditText) appBar.getCustomView().findViewById(R.id.urlTextBox);
-            urlTextBox.setOnKeyListener(new View.OnKeyListener() {
-                public boolean onKey(View v, int keyCode, KeyEvent event) {
-                    // If the event is a key-down event on the "enter" button, load the URL.
-                    if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
-                        // Load the URL into the mainWebView and consume the event.
-                        try {
-                            loadUrlFromTextBox();
-                        } catch (UnsupportedEncodingException e) {
-                            e.printStackTrace();
-                        }
-                        // If the enter key was pressed, consume the event.
-                        return true;
-                    } else {
-                        // If any other key was pressed, do not consume the event.
-                        return false;
-                    }
-                }
-            });
-        }
-
         // Create the navigation drawer.
         drawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
         // The DrawerTitle identifies the drawer in accessibility mode.
@@ -175,12 +208,31 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         // drawerToggle creates the hamburger icon at the start of the AppBar.
         drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, supportAppBar, R.string.open_navigation, R.string.close_navigation);
 
+        // Replace the header that `WebView` creates for `X-Requested-With` with a null value.  The default value is the application ID (com.stoutner.privacybrowser.standard).
+        customHeaders.put("X-Requested-With", "");
+
         mainWebView.setWebViewClient(new WebViewClient() {
-            // shouldOverrideUrlLoading makes this WebView the default handler for URLs inside the app, so that links are not kicked out to other apps.
+            // shouldOverrideUrlLoading makes this `WebView` the default handler for URLs inside the app, so that links are not kicked out to other apps.
             @Override
             public boolean shouldOverrideUrlLoading(WebView view, String url) {
-                mainWebView.loadUrl(url);
-                return true;
+                // Use an external email program if the link begins with "mailto:".
+                if (url.startsWith("mailto:")) {
+                    // We use `ACTION_SENDTO` instead of `ACTION_SEND` so that only email programs are launched.
+                    Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
+
+                    // Parse the url and set it as the data for the `Intent`.
+                    emailIntent.setData(Uri.parse(url));
+
+                    // `FLAG_ACTIVITY_NEW_TASK` opens the email program in a new task instead as part of Privacy Browser.
+                    emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+                    // Make it so.
+                    startActivity(emailIntent);
+                    return true;
+                } else {  // Load the URL in Privacy Browser.
+                    mainWebView.loadUrl(url, customHeaders);
+                    return true;
+                }
             }
 
             // Update the URL in urlTextBox when the page starts to load.
@@ -205,18 +257,15 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
             // Update the progress bar when a page is loading.
             @Override
             public void onProgressChanged(WebView view, int progress) {
-                // Make sure that appBar is not null.
-                if (appBar != null) {
-                    ProgressBar progressBar = (ProgressBar) appBar.getCustomView().findViewById(R.id.progressBar);
-                    progressBar.setProgress(progress);
-                    if (progress < 100) {
-                        progressBar.setVisibility(View.VISIBLE);
-                    } else {
-                        progressBar.setVisibility(View.GONE);
+                ProgressBar progressBar = (ProgressBar) appBar.getCustomView().findViewById(R.id.progressBar);
+                progressBar.setProgress(progress);
+                if (progress < 100) {
+                    progressBar.setVisibility(View.VISIBLE);
+                } else {
+                    progressBar.setVisibility(View.GONE);
 
-                        //Stop the SwipeToRefresh indicator if it is running
-                        swipeToRefresh.setRefreshing(false);
-                    }
+                    //Stop the SwipeToRefresh indicator if it is running
+                    swipeToRefresh.setRefreshing(false);
                 }
             }
 
@@ -226,19 +275,15 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 // Save a copy of the favorite icon for use if a shortcut is added to the home screen.
                 favoriteIcon = icon;
 
-                // Place the favorite icon in the appBar if it is not null.
-                if (appBar != null) {
-                    ImageView imageViewFavoriteIcon = (ImageView) appBar.getCustomView().findViewById(R.id.favoriteIcon);
-                    imageViewFavoriteIcon.setImageBitmap(Bitmap.createScaledBitmap(icon, 64, 64, true));
-                }
+                // Place the favorite icon in the appBar.
+                ImageView imageViewFavoriteIcon = (ImageView) appBar.getCustomView().findViewById(R.id.favoriteIcon);
+                imageViewFavoriteIcon.setImageBitmap(Bitmap.createScaledBitmap(icon, 64, 64, true));
             }
 
             // Enter full screen video
             @Override
             public void onShowCustomView(View view, CustomViewCallback callback) {
-                if (appBar != null) {
-                    appBar.hide();
-                }
+                appBar.hide();
 
                 // Show the fullScreenVideoFrameLayout.
                 assert fullScreenVideoFrameLayout != null; //This assert removes the incorrect warning on the following line that fullScreenVideoFrameLayout might be null.
@@ -255,26 +300,12 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                  * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar across the top of the screen.
                  * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the navigation and status bars ghosted overlays and automatically rehides them.
                  */
-
-                // Set the one flag supported by API >= 14.
-                view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
-
-                // Set the two flags that are supported by API >= 16.
-                if (Build.VERSION.SDK_INT >= 16) {
-                    view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
-                }
-
-                // Set all three flags that are supported by API >= 19.
-                if (Build.VERSION.SDK_INT >= 19) {
-                    view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
-                }
+                view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
             }
 
             // Exit full screen video
             public void onHideCustomView() {
-                if (appBar != null) {
-                    appBar.show();
-                }
+                appBar.show();
 
                 // Show the mainWebView.
                 mainWebView.setVisibility(View.VISIBLE);
@@ -343,6 +374,10 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         domStorageEnabled = savedPreferences.getBoolean("dom_storage_enabled", false);
         mainWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
 
+        // Set the saved form data initial status.  The default is false.
+        saveFormDataEnabled = savedPreferences.getBoolean("save_form_data_enabled", false);
+        mainWebView.getSettings().setSaveFormData(saveFormDataEnabled);
+
         // Set the user agent initial status.
         String userAgentString = savedPreferences.getString("user_agent", "Default user agent");
         switch (userAgentString) {
@@ -403,7 +438,15 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         }
 
         // Load the initial website.
-        mainWebView.loadUrl(formattedUrlString);
+        mainWebView.loadUrl(formattedUrlString, customHeaders);
+
+        // If the favorite icon is null, load the default.
+        if (favoriteIcon == null) {
+            // We have to use `ContextCompat` until API >= 21.
+            Drawable favoriteIconDrawable = ContextCompat.getDrawable(getApplicationContext(), R.drawable.world);
+            BitmapDrawable favoriteIconBitmapDrawable = (BitmapDrawable) favoriteIconDrawable;
+            favoriteIcon = favoriteIconBitmapDrawable.getBitmap();
+        }
 
         // Initialize AdView for the free flavor and request an ad.  If this is not the free flavor BannerAd.requestAd() does nothing.
         adView = findViewById(R.id.adView);
@@ -413,7 +456,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
 
     @Override
     protected void onNewIntent(Intent intent) {
-        // Sets the new intent as the activity intent, so that any future getIntent()s pick up this one instead of creating a new activity.
+        // Sets the new intent as the activity intent, so that any future `getIntent()`s pick up this one instead of creating a new activity.
         setIntent(intent);
 
         if (intent.getData() != null) {
@@ -428,7 +471,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         }
 
         // Load the website.
-        mainWebView.loadUrl(formattedUrlString);
+        mainWebView.loadUrl(formattedUrlString, customHeaders);
 
         // Clear the keyboard if displayed and remove the focus on the urlTextBar if it has it.
         mainWebView.requestFocus();
@@ -437,7 +480,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         // Inflate the menu; this adds items to the action bar if it is present.
-        getMenuInflater().inflate(R.menu.menu_options, menu);
+        getMenuInflater().inflate(R.menu.webview_options_menu, menu);
 
         // Set mainMenu so it can be used by onOptionsItemSelected.
         mainMenu = menu;
@@ -449,6 +492,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         MenuItem toggleFirstPartyCookies = menu.findItem(R.id.toggleFirstPartyCookies);
         MenuItem toggleThirdPartyCookies = menu.findItem(R.id.toggleThirdPartyCookies);
         MenuItem toggleDomStorage = menu.findItem(R.id.toggleDomStorage);
+        MenuItem toggleSaveFormData = menu.findItem(R.id.toggleSaveFormData);
 
         // Set the initial status of the privacy icon.
         updatePrivacyIcon();
@@ -457,6 +501,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         toggleFirstPartyCookies.setChecked(firstPartyCookiesEnabled);
         toggleThirdPartyCookies.setChecked(thirdPartyCookiesEnabled);
         toggleDomStorage.setChecked(domStorageEnabled);
+        toggleSaveFormData.setChecked(saveFormDataEnabled);
 
         return true;
     }
@@ -471,14 +516,23 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
             toggleThirdPartyCookies.setEnabled(false);
         }
 
+        // Enable DOM Storage if JavaScript is enabled.
+        MenuItem toggleDomStorage = menu.findItem(R.id.toggleDomStorage);
+        toggleDomStorage.setEnabled(javaScriptEnabled);
+
         // Enable Clear Cookies if there are any.
         MenuItem clearCookies = menu.findItem(R.id.clearCookies);
         clearCookies.setEnabled(cookieManager.hasCookies());
 
+        // Enable Clear Form Data is there is any.
+        MenuItem clearFormData = menu.findItem(R.id.clearFormData);
+        WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this);
+        clearFormData.setEnabled(mainWebViewDatabase.hasFormData());
+
         // Run all the other default commands.
         super.onPrepareOptionsMenu(menu);
 
-        // return true displays the menu.
+        // `return true` displays the menu.
         return true;
     }
 
@@ -506,7 +560,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 if (javaScriptEnabled) {
                     Snackbar.make(findViewById(R.id.mainWebView), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
                 } else {
-                    if (domStorageEnabled || firstPartyCookiesEnabled) {
+                    if (firstPartyCookiesEnabled) {
                         Snackbar.make(findViewById(R.id.mainWebView), R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
                     } else {
                         Snackbar.make(findViewById(R.id.mainWebView), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
@@ -560,8 +614,19 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 // Apply the new DOM Storage status.
                 mainWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
 
-                // Update the privacy icon.
-                updatePrivacyIcon();
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.toggleSaveFormData:
+                // Switch the status of saveFormDataEnabled.
+                saveFormDataEnabled = !saveFormDataEnabled;
+
+                // Update the menu checkbox.
+                menuItem.setChecked(saveFormDataEnabled);
+
+                // Apply the new form data status.
+                mainWebView.getSettings().setSaveFormData(saveFormDataEnabled);
 
                 // Reload the WebView.
                 mainWebView.reload();
@@ -582,6 +647,12 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 Snackbar.make(findViewById(R.id.mainWebView), R.string.dom_storage_deleted, Snackbar.LENGTH_SHORT).show();
                 return true;
 
+            case R.id.clearFormData:
+                WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this);
+                mainWebViewDatabase.clearFormData();
+                mainWebView.reload();
+                return true;
+
             case R.id.share:
                 Intent shareIntent = new Intent();
                 shareIntent.setAction(Intent.ACTION_SEND);
@@ -591,9 +662,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 return true;
 
             case R.id.addToHomescreen:
-                // Show the CreateHomeScreenShortcut AlertDialog and name this instance createShortcut.
-                AppCompatDialogFragment shortcutDialog = new CreateHomeScreenShortcut();
-                shortcutDialog.show(getSupportFragmentManager(), "createShortcut");
+                // Show the CreateHomeScreenShortcut AlertDialog and name this instance "@string/create_shortcut".
+                DialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcut();
+                createHomeScreenShortcutDialogFragment.show(getFragmentManager(), getResources().getString(R.string.create_shortcut));
 
                 //Everything else will be handled by CreateHomeScreenShortcut and the associated listeners below.
                 return true;
@@ -616,7 +687,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
 
         switch (menuItemId) {
             case R.id.home:
-                mainWebView.loadUrl(homepage);
+                mainWebView.loadUrl(homepage, customHeaders);
                 break;
 
             case R.id.back:
@@ -631,6 +702,12 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 }
                 break;
 
+            case R.id.bookmarks:
+                // Launch BookmarksActivity.
+                Intent bookmarksIntent = new Intent(this, BookmarksActivity.class);
+                startActivity(bookmarksIntent);
+                break;
+
             case R.id.downloads:
                 // Launch the system Download Manager.
                 Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
@@ -642,22 +719,24 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 break;
 
             case R.id.settings:
-                // Launch SettingsActivity.
+                // Launch `SettingsActivity`.
                 Intent settingsIntent = new Intent(this, SettingsActivity.class);
                 startActivity(settingsIntent);
                 break;
 
+            case R.id.guide:
+                // Launch `GuideActivity`.
+                Intent guideIntent = new Intent(this, GuideActivity.class);
+                startActivity(guideIntent);
+                break;
+
             case R.id.about:
-                // Launch AboutActivity.
+                // Launch `AboutActivity`.
                 Intent aboutIntent = new Intent(this, AboutActivity.class);
                 startActivity(aboutIntent);
                 break;
 
             case R.id.clearAndExit:
-                // Clear DOM storage.
-                WebStorage domStorage = WebStorage.getInstance();
-                domStorage.deleteAllData();
-
                 // Clear cookies.  The commands changed slightly in API 21.
                 if (Build.VERSION.SDK_INT >= 21) {
                     cookieManager.removeAllCookies(null);
@@ -665,6 +744,22 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                     cookieManager.removeAllCookie();
                 }
 
+                // Clear DOM storage.
+                WebStorage domStorage = WebStorage.getInstance();
+                domStorage.deleteAllData();
+
+                // Clear form data.
+                WebViewDatabase formData = WebViewDatabase.getInstance(this);
+                formData.clearFormData();
+
+                // Clear cache.  The argument of "true" includes disk files.
+                mainWebView.clearCache(true);
+
+                // Clear the back/forward history.
+                mainWebView.clearHistory();
+
+                formattedUrlString = null;
+
                 // Destroy the internal state of the webview.
                 mainWebView.destroy();
 
@@ -705,14 +800,14 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
     }
 
     @Override
-    public void onCreateHomeScreenShortcutCancel(DialogFragment dialog) {
+    public void onCancelCreateHomeScreenShortcut(DialogFragment dialogFragment) {
         // Do nothing because the user selected "Cancel".
     }
 
     @Override
-    public void onCreateHomeScreenShortcutCreate(DialogFragment dialog) {
+    public void onCreateHomeScreenShortcut(DialogFragment dialogFragment) {
         // Get shortcutNameEditText from the alert dialog.
-        EditText shortcutNameEditText = (EditText) dialog.getDialog().findViewById(R.id.shortcutNameEditText);
+        EditText shortcutNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.shortcut_name_edittext);
 
         // Create the bookmark shortcut based on formattedUrlString.
         Intent bookmarkShortcut = new Intent();
@@ -738,7 +833,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
             drawerLayout.closeDrawer(GravityCompat.START);
         } else {
             // Load the previous URL if available.
-            assert mainWebView != null; //This assert removes the incorrect warning on the following line that mainWebView might be null.
+            assert mainWebView != null; //This assert removes the incorrect warning in Android Studio on the following line that mainWebView might be null.
             if (mainWebView.canGoBack()) {
                 mainWebView.goBack();
             } else {
@@ -765,8 +860,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
     }
 
     private void loadUrlFromTextBox() throws UnsupportedEncodingException {
-        // Get the text from urlTextBox and convert it to a string.
-        String unformattedUrlString = urlTextBox.getText().toString();
+        // Get the text from urlTextBox and convert it to a string.  trim() removes white spaces from the beginning and end of the string.
+        String unformattedUrlString = urlTextBox.getText().toString().trim();
+
         URL unformattedUrl = null;
         Uri.Builder formattedUri = new Uri.Builder();
 
@@ -805,7 +901,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
             }
         }
 
-        mainWebView.loadUrl(formattedUrlString);
+        mainWebView.loadUrl(formattedUrlString, customHeaders);
 
         // Hides the keyboard so we can see the webpage.
         InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
@@ -816,7 +912,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         if (javaScriptEnabled) {
             privacyIcon.setIcon(R.drawable.javascript_enabled);
         } else {
-            if (domStorageEnabled || firstPartyCookiesEnabled) {
+            if (firstPartyCookiesEnabled) {
                 privacyIcon.setIcon(R.drawable.warning);
             } else {
                 privacyIcon.setIcon(R.drawable.privacy_mode);