]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/MainWebViewActivity.java
Remove the third-party cookies icon from the list of additional icons to display...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / MainWebViewActivity.java
index 2919a2229d3297bc5baa13f1a19587ac6805deb6..4433cf386ed82e30037aa660848c5950a5b2ee42 100644 (file)
 package com.stoutner.privacybrowser;
 
 import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
 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.net.http.SslError;
 import android.os.Build;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
-import android.support.v4.app.DialogFragment;
+import android.support.design.widget.NavigationView;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityCompat;
+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;
@@ -44,73 +53,153 @@ import android.view.View;
 import android.view.inputmethod.InputMethodManager;
 import android.webkit.CookieManager;
 import android.webkit.DownloadListener;
+import android.webkit.SslErrorHandler;
 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;
 import android.widget.ProgressBar;
-import android.widget.Toast;
 
 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 CreateHomeScreenShortcut.CreateHomeScreenSchortcutListener {
-    // favoriteIcon is public static so it can be accessed from CreateHomeScreenShortcut.
+public class MainWebViewActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, CreateHomeScreenShortcut.CreateHomeScreenSchortcutListener,
+        SslCertificateError.SslCertificateErrorListener, DownloadFile.DownloadFileListener {
+    // `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 AboutDialog.  It is also used in onCreate(), onOptionsItemSelected(), and loadUrlFromTextBox().
+
+    // `privacyBrowserActivity` is public static so it can be accessed from `SettingsFragment`.
+    // It is also used in `onCreate()`, `onCreateOptionsMenu()`, and `onOptionsItemSelected()`,
+    public static Activity privacyBrowserActivity;
+
+    // `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 used in onCreateOptionsMenu() and onOptionsItemSelected().
-    private Menu mainMenu;
-    // formattedUrlString is used in onCreate(), onOptionsItemSelected(), onCreateHomeScreenShortcutCreate(), and loadUrlFromTextBox().
-    private String formattedUrlString;
-    // homepage is used in onCreate() and onOptionsItemSelected().
-    private String homepage;
-    // javaScriptEnabled is used in onCreate(), onCreateOptionsMenu(), onOptionsItemSelected(), and loadUrlFromTextBox().
-    private boolean javaScriptEnabled;
-    // domStorageEnabled is used in onCreate(), onCreateOptionsMenu(), and onOptionsItemSelected().
-    private boolean domStorageEnabled;
-
-    /* saveFormDataEnabled does nothing until database storage is implemented.
-    // saveFormDataEnabled is used in onCreate(), onCreateOptionsMenu(), and onOptionsItemSelected().
-    private boolean saveFormDataEnabled;
-    */
-
-    // cookieManager is used in onCreate() and onOptionsItemSelected().
-    private CookieManager cookieManager;
-    // cookiesEnabled is used in onCreate(), onCreateOptionsMenu(), and onOptionsItemSelected().
-    private boolean cookiesEnabled;
-
-    // urlTextBox is used in onCreate(), onOptionsItemSelected(), and loadUrlFromTextBox().
+    // `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()`.
+    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()`.
+    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()`.
+    public static boolean firstPartyCookiesEnabled;
+
+    // `thirdPartyCookiesEnables` is public static so it can be accessed from `SettingsFragment`.
+    // It is also 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()`.
+    public static boolean domStorageEnabled;
+
+    // `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()`.
+    public static String javaScriptEnabledSearchURL;
+
+    // `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().
+    public static SwipeRefreshLayout swipeToRefresh;
+
+    // `swipeToRefreshEnabled` is public static so it can be accessed from `SettingsFragment`.  It is also used in `onCreate()`.
+    public static boolean swipeToRefreshEnabled;
+
+    // `customHeader` is public static so it can be accessed from `BookmarksActivity`.  It is also used in `onCreate()`, `onOptionsItemSelected()`, 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()`.
+    private DrawerLayout drawerLayout;
+
+    // `urlTextBox` is used in `onCreate()`, `onOptionsItemSelected()`, and `loadUrlFromTextBox()`.
     private EditText urlTextBox;
 
+    // `adView` is used in `onCreate()` and `onConfigurationChanged()`.
+    private View adView;
+
+    // `sslErrorHandler` is used in `onCreate()`, `onSslErrorCancel()`, and `onSslErrorProceed`.
+    private SslErrorHandler sslErrorHandler;
+
+    // `sharedPreferences` is used in `onCreate()` and `onCreateOptionsMenu()`.
+    SharedPreferences sharedPreferences;
+
     @Override
     // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled.  The whole premise of Privacy Browser is built around an understanding of these dangers.
     @SuppressLint("SetJavaScriptEnabled")
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_webview);
+        setContentView(R.layout.main_coordinatorlayout);
+
+        // We need a handle for the activity, which is accessed from `SettingsFragment` and fed into `updatePrivacyIcons()`.
+        privacyBrowserActivity = this;
 
         // 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 FrameLayout fullScreenVideoFrameLayout = (FrameLayout) findViewById(R.id.fullScreenVideoFrameLayout);
-        final Activity mainWebViewActivity = this;
-        // We need to use the SupportActionBar from android.support.v7.app.ActionBar until the minimum API is >= 21.
         final ActionBar appBar = getSupportActionBar();
 
-        // Setup AdView for the free flavor.
-        final View adView = findViewById(R.id.adView);
+        // 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;
+                }
+            }
+        });
+
+        final FrameLayout fullScreenVideoFrameLayout = (FrameLayout) findViewById(R.id.fullScreenVideoFrameLayout);
 
         // Implement swipe to refresh
-        final SwipeRefreshLayout swipeToRefresh = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
+        swipeToRefresh = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
+        assert swipeToRefresh != null; //This assert removes the incorrect warning on the following line that swipeToRefresh might be null.
         swipeToRefresh.setColorSchemeResources(R.color.blue);
         swipeToRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
             @Override
@@ -121,46 +210,42 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
 
         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.
+        drawerLayout.setDrawerTitle(GravityCompat.START, getString(R.string.navigation_drawer));
+
+        // Listen for touches on the navigation menu.
+        final NavigationView navigationView = (NavigationView) findViewById(R.id.navigationView);
+        assert navigationView != null; // This assert removes the incorrect warning on the following line that navigationView might be null.
+        navigationView.setNavigationItemSelectedListener(this);
+
+        // 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);
 
         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;
-            }
-
-            /* These errors do not provide any useful information and clutter the screen.
-            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
-                Toast.makeText(mainWebViewActivity, "Error loading " + request + "   Error: " + error, Toast.LENGTH_SHORT).show();
+                // 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.
             @Override
@@ -172,7 +257,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
             @Override
             public void onPageFinished(WebView view, String url) {
                 formattedUrlString = url;
-                urlTextBox.setText(formattedUrlString);
+
+                // Only update urlTextBox if the user is not typing in it.
+                if (!urlTextBox.hasFocus()) {
+                    urlTextBox.setText(formattedUrlString);
+                }
+            }
+
+            // Handle SSL Certificate errors.
+            @Override
+            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+                // Store `handler` so it can be accesses from `onSslErrorCancel()` and `onSslErrorProceed()`.
+                sslErrorHandler = handler;
+
+                // Display the SSL error `AlertDialog`.
+                DialogFragment sslCertificateErrorDialogFragment = SslCertificateError.displayDialog(error);
+                sslCertificateErrorDialogFragment.show(getFragmentManager(), getResources().getString(R.string.ssl_certificate_error));
             }
         });
 
@@ -180,18 +280,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
             // 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);
                 }
             }
 
@@ -201,21 +298,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
                 // 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.
                 fullScreenVideoFrameLayout.addView(view);
                 fullScreenVideoFrameLayout.setVisibility(View.VISIBLE);
 
@@ -226,31 +320,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
                 BannerAd.hideAd(adView);
 
                 /* SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bars on the bottom or right of the screen.
-                ** 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.
-                if (Build.VERSION.SDK_INT >= 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);
-                }
+                 * 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.
+                 */
+                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);
@@ -259,6 +337,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
                 BannerAd.showAd(adView);
 
                 // Hide the fullScreenVideoFrameLayout.
+                assert fullScreenVideoFrameLayout != null; //This assert removes the incorrect warning on the following line that fullScreenVideoFrameLayout might be null.
                 fullScreenVideoFrameLayout.removeAllViews();
                 fullScreenVideoFrameLayout.setVisibility(View.GONE);
             }
@@ -266,60 +345,109 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
 
         // Allow the downloading of files.
         mainWebView.setDownloadListener(new DownloadListener() {
-            // Launch the Android download manager when a link leads to a download.
             @Override
             public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
-                DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
-                DownloadManager.Request requestUri = new DownloadManager.Request(Uri.parse(url));
-
-                // Add the URL as the description for the download.
-                requestUri.setDescription(url);
-
-                // Show the download notification after the download is completed if the API is 11 or greater.
-                if (Build.VERSION.SDK_INT >= 11) {
-                    requestUri.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
-                }
-
-                downloadManager.enqueue(requestUri);
-                Toast.makeText(mainWebViewActivity, R.string.download_started, Toast.LENGTH_SHORT).show();
+                // Show the `DownloadFile` `AlertDialog` and name this instance `@string/download`.
+                DialogFragment downloadFileDialogFragment = DownloadFile.fromUrl(url, contentDisposition, contentLength);
+                downloadFileDialogFragment.show(getFragmentManager(), getResources().getString(R.string.file_download));
             }
         });
 
         // Allow pinch to zoom.
         mainWebView.getSettings().setBuiltInZoomControls(true);
 
-        // Hide zoom controls if the API is 11 or greater.
-        if (Build.VERSION.SDK_INT >= 11) {
-            mainWebView.getSettings().setDisplayZoomControls(false);
-        }
+        // Hide zoom controls.
+        mainWebView.getSettings().setDisplayZoomControls(false);
+
 
         // Initialize the default preference values the first time the program is run.
         PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
 
         // Get the shared preference values.
-        SharedPreferences savedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+        sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
-        // Set JavaScript initial status.
-        javaScriptEnabled = savedPreferences.getBoolean("javascript_enabled", false);
+        // Set JavaScript initial status.  The default value is false.
+        javaScriptEnabled = sharedPreferences.getBoolean("javascript_enabled", false);
         mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
 
-        // Set DOM storage initial status.
-        domStorageEnabled = savedPreferences.getBoolean("dom_storage_enabled", false);
+        // Initialize cookieManager.
+        cookieManager = CookieManager.getInstance();
+
+        // Set cookies initial status.  The default value is false.
+        firstPartyCookiesEnabled = sharedPreferences.getBoolean("first_party_cookies_enabled", false);
+        cookieManager.setAcceptCookie(firstPartyCookiesEnabled);
+
+        // Set third-party cookies initial status if API >= 21.  The default value is false.
+        if (Build.VERSION.SDK_INT >= 21) {
+            thirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies_enabled", false);
+            cookieManager.setAcceptThirdPartyCookies(mainWebView, thirdPartyCookiesEnabled);
+        }
+
+        // Set DOM storage initial status.  The default value is false.
+        domStorageEnabled = sharedPreferences.getBoolean("dom_storage_enabled", false);
         mainWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
 
-        /* Save Form Data does nothing until database storage is implemented.
-        // Set Save Form Data initial status.
-        saveFormDataEnabled = true;
+        // Set the saved form data initial status.  The default is false.
+        saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data_enabled", false);
         mainWebView.getSettings().setSaveFormData(saveFormDataEnabled);
-        */
 
-        // Set cookies initial status.
-        cookiesEnabled = savedPreferences.getBoolean("cookies_enabled", false);
-        cookieManager = CookieManager.getInstance();
-        cookieManager.setAcceptCookie(cookiesEnabled);
+        // Set the user agent initial status.
+        String userAgentString = sharedPreferences.getString("user_agent", "Default user agent");
+        switch (userAgentString) {
+            case "Default user agent":
+                // Do nothing.
+                break;
+
+            case "Custom user agent":
+                // Set the custom user agent on mainWebView,  The default is "PrivacyBrowser/1.0".
+                mainWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0"));
+                break;
+
+            default:
+                // Set the selected user agent on mainWebView.  The default is "PrivacyBrowser/1.0".
+                mainWebView.getSettings().setUserAgentString(sharedPreferences.getString("user_agent", "PrivacyBrowser/1.0"));
+                break;
+        }
+
+        // Set the initial string for JavaScript disabled search.
+        if (sharedPreferences.getString("javascript_disabled_search", "https://duckduckgo.com/html/?q=").equals("Custom URL")) {
+            // Get the custom URL string.  The default is "".
+            javaScriptDisabledSearchURL = sharedPreferences.getString("javascript_disabled_search_custom_url", "");
+        } else {
+            // Use the string from javascript_disabled_search.
+            javaScriptDisabledSearchURL = sharedPreferences.getString("javascript_disabled_search", "https://duckduckgo.com/html/?q=");
+        }
+
+        // Set the initial string for JavaScript enabled search.
+        if (sharedPreferences.getString("javascript_enabled_search", "https://duckduckgo.com/?q=").equals("Custom URL")) {
+            // Get the custom URL string.  The default is "".
+            javaScriptEnabledSearchURL = sharedPreferences.getString("javascript_enabled_search_custom_url", "");
+        } else {
+            // Use the string from javascript_enabled_search.
+            javaScriptEnabledSearchURL = sharedPreferences.getString("javascript_enabled_search", "https://duckduckgo.com/?q=");
+        }
+
+
+        // Set the homepage initial status.  The default value is `https://www.duckduckgo.com`.
+        homepage = sharedPreferences.getString("homepage", "https://www.duckduckgo.com");
+
+        // Set the font size initial status.  the default value is `100`.
+        String defaultFontSizeString = sharedPreferences.getString("default_font_size", "100");
+        mainWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
+
+        // Set the swipe to refresh initial status.  The default is `true`.
+        swipeToRefreshEnabled = sharedPreferences.getBoolean("swipe_to_refresh_enabled", true);
+        swipeToRefresh.setEnabled(swipeToRefreshEnabled);
+
+
+        // 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", "");
+
+        // Set Do Not Track.  The default is true.
+        if (sharedPreferences.getBoolean("do_not_track", true)) {
+            customHeaders.put("DNT", "1");
+        }
 
-        // Set homepage initial status.
-        homepage = savedPreferences.getString("homepage", "https://www.duckduckgo.com");
 
         // Get the intent information that started the app.
         final Intent intent = getIntent();
@@ -336,15 +464,25 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
         }
 
         // 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();
+        }
 
-        // Load the ad if this is the free flavor.
+        // 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);
         BannerAd.requestAd(adView);
     }
 
+
     @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) {
@@ -353,8 +491,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
             formattedUrlString = intentUriData.toString();
         }
 
+        // Close the navigation drawer if it is open.
+        if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
+            drawerLayout.closeDrawer(GravityCompat.START);
+        }
+
         // 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();
@@ -363,64 +506,128 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
     @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_webview, menu);
+        getMenuInflater().inflate(R.menu.webview_options_menu, menu);
 
         // Set mainMenu so it can be used by onOptionsItemSelected.
         mainMenu = menu;
 
-        // Get MenuItems for checkable menu items.
-        MenuItem toggleJavaScript = menu.findItem(R.id.toggleJavaScript);
+        // Set the initial status of the privacy icon.
+        updatePrivacyIcons(privacyBrowserActivity);
+
+        // Get handles for the menu items.
+        MenuItem toggleFirstPartyCookies = menu.findItem(R.id.toggleFirstPartyCookies);
+        MenuItem toggleThirdPartyCookies = menu.findItem(R.id.toggleThirdPartyCookies);
         MenuItem toggleDomStorage = menu.findItem(R.id.toggleDomStorage);
-        /* toggleSaveFormData does nothing until database storage is implemented.
         MenuItem toggleSaveFormData = menu.findItem(R.id.toggleSaveFormData);
-        */
-        MenuItem toggleCookies = menu.findItem(R.id.toggleCookies);
-
-        // Set the initial icon for toggleJavaScript
-        if (javaScriptEnabled) {
-            toggleJavaScript.setIcon(R.drawable.javascript_enabled);
-        } else {
-            if (domStorageEnabled || cookiesEnabled) {
-                toggleJavaScript.setIcon(R.drawable.warning);
-            } else {
-                toggleJavaScript.setIcon(R.drawable.privacy_mode);
-            }
-        }
 
         // Set the initial status of the menu item checkboxes.
+        toggleFirstPartyCookies.setChecked(firstPartyCookiesEnabled);
+        toggleThirdPartyCookies.setChecked(thirdPartyCookiesEnabled);
         toggleDomStorage.setChecked(domStorageEnabled);
-        /* toggleSaveFormData does nothing until database storage is implemented.
         toggleSaveFormData.setChecked(saveFormDataEnabled);
-        */
-        toggleCookies.setChecked(cookiesEnabled);
+
+        // Set the status of the additional app bar icons.  The default is `false`.
+        if (sharedPreferences.getBoolean("display_additional_app_bar_icons", false)) {
+            toggleFirstPartyCookies.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            toggleDomStorage.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            toggleSaveFormData.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+        } else { //Do not display the additional icons.
+            toggleFirstPartyCookies.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+            toggleDomStorage.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+            toggleSaveFormData.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+        }
 
         return true;
     }
 
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
+        // Only enable Third-Party Cookies if SDK >= 21 and First-Party Cookies are enabled.
+        MenuItem toggleThirdPartyCookies = menu.findItem(R.id.toggleThirdPartyCookies);
+        if ((Build.VERSION.SDK_INT >= 21) && firstPartyCookiesEnabled) {
+            toggleThirdPartyCookies.setEnabled(true);
+        } else {
+            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 Back if canGoBack().
-        MenuItem back = menu.findItem(R.id.back);
-        back.setEnabled(mainWebView.canGoBack());
+        // Enable Clear Form Data is there is any.
+        MenuItem clearFormData = menu.findItem(R.id.clearFormData);
+        WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this);
+        clearFormData.setEnabled(mainWebViewDatabase.hasFormData());
+
+        // Initialize font size variables.
+        int fontSize = mainWebView.getSettings().getTextZoom();
+        String fontSizeTitle;
+        MenuItem selectedFontSizeMenuItem;
+
+        // Prepare the font size title and current size menu item.
+        switch (fontSize) {
+            case 50:
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.fifty_percent);
+                selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeFiftyPercent);
+                break;
+
+            case 75:
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.seventy_five_percent);
+                selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeSeventyFivePercent);
+                break;
+
+            case 100:
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_percent);
+                selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeOneHundredPercent);
+                break;
+
+            case 125:
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_twenty_five_percent);
+                selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeOneHundredTwentyFivePercent);
+                break;
+
+            case 150:
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_fifty_percent);
+                selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeOneHundredFiftyPercent);
+                break;
+
+            case 175:
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_seventy_five_percent);
+                selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeOneHundredSeventyFivePercent);
+                break;
+
+            case 200:
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.two_hundred_percent);
+                selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeTwoHundredPercent);
+                break;
 
-        // Enable forward if canGoForward().
-        MenuItem forward = menu.findItem(R.id.forward);
-        forward.setEnabled(mainWebView.canGoForward());
+            default:
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_percent);
+                selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeOneHundredPercent);
+                break;
+        }
+
+        // Set the font size title and select the current size menu item.
+        MenuItem fontSizeMenuItem = menu.findItem(R.id.fontSize);
+        fontSizeMenuItem.setTitle(fontSizeTitle);
+        selectedFontSizeMenuItem.setChecked(true);
+
+        // Only show `Refresh` if `swipeToRefresh` is disabled.
+        MenuItem refreshMenuItem = menu.findItem(R.id.refresh);
+        refreshMenuItem.setVisible(!swipeToRefreshEnabled);
 
         // Run all the other default commands.
         super.onPrepareOptionsMenu(menu);
 
-        // return true displays the menu.
+        // `return true` displays the menu.
         return true;
     }
 
     @Override
-    // @TargetApi(11) turns off the errors regarding copy and paste, which are removed from view in menu_webview.xml for lower version of Android.
-    @TargetApi(11)
     // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled.
     @SuppressLint("SetJavaScriptEnabled")
     // removeAllCookies is deprecated, but it is required for API < 21.
@@ -428,126 +635,124 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
     public boolean onOptionsItemSelected(MenuItem menuItem) {
         int menuItemId = menuItem.getItemId();
 
-        // Some options need to update the drawable for toggleJavaScript.
-        MenuItem toggleJavaScript = mainMenu.findItem(R.id.toggleJavaScript);
-
         // Set the commands that relate to the menu entries.
         switch (menuItemId) {
             case R.id.toggleJavaScript:
-                if (javaScriptEnabled) {
-                    javaScriptEnabled = false;
-                    mainWebView.getSettings().setJavaScriptEnabled(false);
-                    mainWebView.reload();
+                // Switch the status of javaScriptEnabled.
+                javaScriptEnabled = !javaScriptEnabled;
+
+                // Apply the new JavaScript status.
+                mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
 
-                    // Update the toggleJavaScript icon and display a toast message.
-                    if (domStorageEnabled || cookiesEnabled) {
-                        menuItem.setIcon(R.drawable.warning);
-                        if (domStorageEnabled && cookiesEnabled) {
-                            Toast.makeText(getApplicationContext(), "JavaScript disabled, DOM Storage and Cookies still enabled", Toast.LENGTH_SHORT).show();
-                        } else {
-                            if (domStorageEnabled) {
-                                Toast.makeText(getApplicationContext(), "JavaScript disabled, DOM Storage still enabled", Toast.LENGTH_SHORT).show();
-                            } else {
-                                Toast.makeText(getApplicationContext(), "JavaScript disabled, Cookies still enabled", Toast.LENGTH_SHORT).show();
-                            }
-                        }
+                // Update the privacy icon.
+                updatePrivacyIcons(privacyBrowserActivity);
+
+                // Display a `Snackbar`.
+                if (javaScriptEnabled) {
+                    Snackbar.make(findViewById(R.id.mainWebView), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
+                } else {
+                    if (firstPartyCookiesEnabled) {
+                        Snackbar.make(findViewById(R.id.mainWebView), R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
                     } else {
-                        menuItem.setIcon(R.drawable.privacy_mode);
-                        Toast.makeText(getApplicationContext(), R.string.privacy_mode, Toast.LENGTH_SHORT).show();
+                        Snackbar.make(findViewById(R.id.mainWebView), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
                     }
-                } else {
-                    javaScriptEnabled = true;
-                    menuItem.setIcon(R.drawable.javascript_enabled);
-                    mainWebView.getSettings().setJavaScriptEnabled(true);
-                    mainWebView.reload();
-                    Toast.makeText(getApplicationContext(), "JavaScript enabled", Toast.LENGTH_SHORT).show();
                 }
+
+                // Reload the WebView.
+                mainWebView.reload();
                 return true;
 
-            case R.id.toggleDomStorage:
-                if (domStorageEnabled) {
-                    domStorageEnabled = false;
-                    menuItem.setChecked(false);
-                    mainWebView.getSettings().setDomStorageEnabled(false);
-                    mainWebView.reload();
+            case R.id.toggleFirstPartyCookies:
+                // Switch the status of firstPartyCookiesEnabled.
+                firstPartyCookiesEnabled = !firstPartyCookiesEnabled;
 
-                    // Update the toggleJavaScript icon and display a toast message if appropriate.
-                    if (!javaScriptEnabled && !cookiesEnabled) {
-                        toggleJavaScript.setIcon(R.drawable.privacy_mode);
-                        Toast.makeText(getApplicationContext(), R.string.privacy_mode, Toast.LENGTH_SHORT).show();
-                    } else {
-                        if (cookiesEnabled) {
-                            toggleJavaScript.setIcon(R.drawable.warning);
-                            Toast.makeText(getApplicationContext(), "Cookies still enabled", Toast.LENGTH_SHORT).show();
-                        } // Else Do nothing because JavaScript is enabled.
-                    }
-                } else {
-                    domStorageEnabled = true;
-                    menuItem.setChecked(true);
-                    mainWebView.getSettings().setDomStorageEnabled(true);
-                    mainWebView.reload();
+                // Update the menu checkbox.
+                menuItem.setChecked(firstPartyCookiesEnabled);
 
-                    // Update the toggleJavaScript icon if appropriate.
-                    if (!javaScriptEnabled) {
-                        toggleJavaScript.setIcon(R.drawable.warning);
-                    } // Else Do nothing because JavaScript is enabled.
+                // Apply the new cookie status.
+                cookieManager.setAcceptCookie(firstPartyCookiesEnabled);
 
-                    Toast.makeText(getApplicationContext(), "DOM Storage enabled", Toast.LENGTH_SHORT).show();
-                }
-                return true;
+                // Update the privacy icon.
+                updatePrivacyIcons(privacyBrowserActivity);
 
-            /* toggleSaveFormData does nothing until database storage is implemented.
-            case R.id.toggleSaveFormData:
-                if (saveFormDataEnabled) {
-                    saveFormDataEnabled = false;
-                    menuItem.setChecked(false);
-                    mainWebView.getSettings().setSaveFormData(false);
-                    mainWebView.reload();
+                // Display a `Snackbar`.
+                if (firstPartyCookiesEnabled) {
+                    Snackbar.make(findViewById(R.id.mainWebView), R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
                 } else {
-                    saveFormDataEnabled = true;
-                    menuItem.setChecked(true);
-                    mainWebView.getSettings().setSaveFormData(true);
-                    mainWebView.reload();
+                    if (javaScriptEnabled) {
+                        Snackbar.make(findViewById(R.id.mainWebView), R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
+                    } else {
+                        Snackbar.make(findViewById(R.id.mainWebView), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
+                    }
                 }
+
+                // Reload the WebView.
+                mainWebView.reload();
                 return true;
-            */
 
-            case R.id.toggleCookies:
-                if (cookiesEnabled) {
-                    cookiesEnabled = false;
-                    menuItem.setChecked(false);
-                    cookieManager.setAcceptCookie(false);
-                    mainWebView.reload();
+            case R.id.toggleThirdPartyCookies:
+                if (Build.VERSION.SDK_INT >= 21) {
+                    // Switch the status of thirdPartyCookiesEnabled.
+                    thirdPartyCookiesEnabled = !thirdPartyCookiesEnabled;
+
+                    // Update the menu checkbox.
+                    menuItem.setChecked(thirdPartyCookiesEnabled);
 
-                    // Update the toggleJavaScript icon and display a toast message if appropriate.
-                    if (!javaScriptEnabled && !domStorageEnabled) {
-                        toggleJavaScript.setIcon(R.drawable.privacy_mode);
-                        Toast.makeText(getApplicationContext(), R.string.privacy_mode, Toast.LENGTH_SHORT).show();
+                    // Apply the new cookie status.
+                    cookieManager.setAcceptThirdPartyCookies(mainWebView, thirdPartyCookiesEnabled);
+
+                    // Display a `Snackbar`.
+                    if (thirdPartyCookiesEnabled) {
+                        Snackbar.make(findViewById(R.id.mainWebView), R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
                     } else {
-                        if (domStorageEnabled) {
-                            toggleJavaScript.setIcon(R.drawable.warning);
-                            Toast.makeText(getApplicationContext(), "DOM Storage still enabled", Toast.LENGTH_SHORT).show();
-                        } // Else Do nothing because JavaScript is enabled.
+                        Snackbar.make(findViewById(R.id.mainWebView), R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
                     }
-                } else {
-                    cookiesEnabled = true;
-                    menuItem.setChecked(true);
-                    cookieManager.setAcceptCookie(true);
+
+                    // Reload the WebView.
                     mainWebView.reload();
+                } // Else do nothing because SDK < 21.
+                return true;
+
+            case R.id.toggleDomStorage:
+                // Switch the status of domStorageEnabled.
+                domStorageEnabled = !domStorageEnabled;
+
+                // Update the menu checkbox.
+                menuItem.setChecked(domStorageEnabled);
 
-                    // Update the toggleJavaScript icon if appropriate.
-                    if (!javaScriptEnabled) {
-                        toggleJavaScript.setIcon(R.drawable.warning);
-                    } // Else Do nothing because JavaScript is enabled.
+                // Apply the new DOM Storage status.
+                mainWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
 
-                    Toast.makeText(getApplicationContext(), "Cookies enabled", Toast.LENGTH_SHORT).show();
+                // Display a `Snackbar`.
+                if (domStorageEnabled) {
+                    Snackbar.make(findViewById(R.id.mainWebView), R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show();
+                } else {
+                    Snackbar.make(findViewById(R.id.mainWebView), R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show();
                 }
+
+                // Reload the WebView.
+                mainWebView.reload();
                 return true;
 
-            case R.id.clearDomStorage:
-                WebStorage webStorage = WebStorage.getInstance();
-                webStorage.deleteAllData();
-                Toast.makeText(getApplicationContext(), "DOM storage deleted", Toast.LENGTH_SHORT).show();
+            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);
+
+                // Display a `Snackbar`.
+                if (saveFormDataEnabled) {
+                    Snackbar.make(findViewById(R.id.mainWebView), R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show();
+                } else {
+                    Snackbar.make(findViewById(R.id.mainWebView), R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show();
+                }
+
+                // Reload the WebView.
+                mainWebView.reload();
                 return true;
 
             case R.id.clearCookies:
@@ -556,37 +761,47 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
                 } else {
                     cookieManager.removeAllCookies(null);
                 }
-                Toast.makeText(getApplicationContext(), "Cookies deleted", Toast.LENGTH_SHORT).show();
+                Snackbar.make(findViewById(R.id.mainWebView), R.string.cookies_deleted, Snackbar.LENGTH_SHORT).show();
                 return true;
 
-            case R.id.addToHomescreen:
-                // Show the CreateHomeScreenShortcut AlertDialog and name this instance createShortcut.
-                AppCompatDialogFragment shortcutDialog = new CreateHomeScreenShortcut();
-                shortcutDialog.show(getSupportFragmentManager(), "createShortcut");
+            case R.id.clearDomStorage:
+                WebStorage webStorage = WebStorage.getInstance();
+                webStorage.deleteAllData();
+                Snackbar.make(findViewById(R.id.mainWebView), R.string.dom_storage_deleted, Snackbar.LENGTH_SHORT).show();
+                return true;
 
-                //Everything else will be handled by CreateHomeScreenShortcut and the associated listeners below.
+            case R.id.clearFormData:
+                WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this);
+                mainWebViewDatabase.clearFormData();
+                mainWebView.reload();
                 return true;
 
-            case R.id.downloads:
-                // Launch the system Download Manager.
-                Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
+            case R.id.fontSizeFiftyPercent:
+                mainWebView.getSettings().setTextZoom(50);
+                return true;
 
-                // Launch as a new task so that Download Manager and Privacy Browser show as separate windows in the recent tasks list.
-                downloadManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            case R.id.fontSizeSeventyFivePercent:
+                mainWebView.getSettings().setTextZoom(75);
+                return true;
 
-                startActivity(downloadManagerIntent);
+            case R.id.fontSizeOneHundredPercent:
+                mainWebView.getSettings().setTextZoom(100);
                 return true;
 
-            case R.id.home:
-                mainWebView.loadUrl(homepage);
+            case R.id.fontSizeOneHundredTwentyFivePercent:
+                mainWebView.getSettings().setTextZoom(125);
                 return true;
 
-            case R.id.back:
-                mainWebView.goBack();
+            case R.id.fontSizeOneHundredFiftyPercent:
+                mainWebView.getSettings().setTextZoom(150);
                 return true;
 
-            case R.id.forward:
-                mainWebView.goForward();
+            case R.id.fontSizeOneHundredSeventyFivePercent:
+                mainWebView.getSettings().setTextZoom(175);
+                return true;
+
+            case R.id.fontSizeTwoHundredPercent:
+                mainWebView.getSettings().setTextZoom(200);
                 return true;
 
             case R.id.share:
@@ -597,23 +812,82 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
                 startActivity(Intent.createChooser(shareIntent, "Share URL"));
                 return true;
 
-            case R.id.settings:
-                // Launch SettingsActivity.
-                Intent intent = new Intent(this, SettingsActivity.class);
-                startActivity(intent);
+            case R.id.addToHomescreen:
+                // 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 listener below.
                 return true;
 
-            case R.id.about:
-                // Show the AboutDialog AlertDialog and name this instance aboutDialog.
-                AppCompatDialogFragment aboutDialog = new AboutDialog();
-                aboutDialog.show(getSupportFragmentManager(), "aboutDialog");
+            case R.id.refresh:
+                mainWebView.reload();
                 return true;
 
-            case R.id.clearAndExit:
-                // Clear DOM storage.
-                WebStorage domStorage = WebStorage.getInstance();
-                domStorage.deleteAllData();
+            default:
+                // Don't consume the event.
+                return super.onOptionsItemSelected(menuItem);
+        }
+    }
+
+    @Override
+    // removeAllCookies is deprecated, but it is required for API < 21.
+    @SuppressWarnings("deprecation")
+    public boolean onNavigationItemSelected(MenuItem menuItem) {
+        int menuItemId = menuItem.getItemId();
+
+        switch (menuItemId) {
+            case R.id.home:
+                mainWebView.loadUrl(homepage, customHeaders);
+                break;
+
+            case R.id.back:
+                if (mainWebView.canGoBack()) {
+                    mainWebView.goBack();
+                }
+                break;
+
+            case R.id.forward:
+                if (mainWebView.canGoForward()) {
+                    mainWebView.goForward();
+                }
+                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);
 
+                // Launch as a new task so that Download Manager and Privacy Browser show as separate windows in the recent tasks list.
+                downloadManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+                startActivity(downloadManagerIntent);
+                break;
+
+            case R.id.settings:
+                // 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`.
+                Intent aboutIntent = new Intent(this, AboutActivity.class);
+                startActivity(aboutIntent);
+                break;
+
+            case R.id.clearAndExit:
                 // Clear cookies.  The commands changed slightly in API 21.
                 if (Build.VERSION.SDK_INT >= 21) {
                     cookieManager.removeAllCookies(null);
@@ -621,6 +895,29 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
                     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();
+
+                // Clear any SSL certificate preferences.
+                MainWebViewActivity.mainWebView.clearSslPreferences();
+
+                // Clear `formattedUrlString`.
+                formattedUrlString = null;
+
+                // Clear `customHeaders`.
+                customHeaders.clear();
+
                 // Destroy the internal state of the webview.
                 mainWebView.destroy();
 
@@ -630,22 +927,43 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
                 } else {
                     finish();
                 }
-                return true;
+                break;
 
             default:
-                return super.onOptionsItemSelected(menuItem);
+                break;
         }
+
+        // Close the navigation drawer.
+        drawerLayout.closeDrawer(GravityCompat.START);
+        return true;
+    }
+
+    @Override
+    public void onPostCreate(Bundle savedInstanceState) {
+        super.onPostCreate(savedInstanceState);
+
+        // Sync the state of the DrawerToggle after onRestoreInstanceState has finished.
+        drawerToggle.syncState();
     }
 
     @Override
-    public void onCreateHomeScreenShortcutCancel(DialogFragment dialog) {
-        // Do nothing because the user selected "Cancel".
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        // Reload the ad if this is the free flavor.
+        BannerAd.reloadAfterRotate(adView, getApplicationContext(), getString(R.string.ad_id));
+
+        // Reinitialize the adView variable, as the View will have been removed and re-added in the free flavor by BannerAd.reloadAfterRotate().
+        adView = findViewById(R.id.adView);
+
+        // `invalidateOptionsMenu` should recalculate the number of action buttons from the menu to display on the app bar, but it doesn't because of the this bug:  https://code.google.com/p/android/issues/detail?id=20493#c8
+        invalidateOptionsMenu();
     }
 
     @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();
@@ -661,21 +979,88 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
         sendBroadcast(placeBookmarkShortcut);
     }
 
-    // Override onBackPressed so that if mainWebView can go back it does when the system back button is pressed.
+    @Override
+    public void onDownloadFile(DialogFragment dialogFragment, String downloadUrl) {
+        DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
+        DownloadManager.Request downloadRequest = new DownloadManager.Request(Uri.parse(downloadUrl));
+
+        // Get the file name from `dialogFragment`.
+        EditText downloadFileNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.download_file_name);
+        String fileName = downloadFileNameEditText.getText().toString();
+
+        // Set the download save in the the `DIRECTORY_DOWNLOADS`using `fileName`.
+        // Once we have `WRITE_EXTERNAL_STORAGE` permissions we can use `setDestinationInExternalPublicDir`.
+        downloadRequest.setDestinationInExternalFilesDir(this, "/", fileName);
+
+        // Allow `MediaScanner` to index the download if it is a media file.
+        downloadRequest.allowScanningByMediaScanner();
+
+        // Add the URL as the description for the download.
+        downloadRequest.setDescription(downloadUrl);
+
+        // Show the download notification after the download is completed.
+        downloadRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
+
+        // Initiate the download and display a Snackbar.
+        downloadManager.enqueue(downloadRequest);
+    }
+
+    public void viewSslCertificate(View view) {
+        // Show the `ViewSslCertificate` `AlertDialog` and name this instance `@string/view_ssl_certificate`.
+        DialogFragment viewSslCertificateDialogFragment = new ViewSslCertificate();
+        viewSslCertificateDialogFragment.show(getFragmentManager(), getResources().getString(R.string.view_ssl_certificate));
+    }
+
+    @Override
+    public void onSslErrorCancel() {
+        sslErrorHandler.cancel();
+    }
+
+    @Override
+    public void onSslErrorProceed() {
+        sslErrorHandler.proceed();
+    }
+
+    // Override onBackPressed to handle the navigation drawer and mainWebView.
     @Override
     public void onBackPressed() {
         final WebView mainWebView = (WebView) findViewById(R.id.mainWebView);
 
-        if (mainWebView.canGoBack()) {
-            mainWebView.goBack();
+        // Close the navigation drawer if it is available.  GravityCompat.START is the drawer on the left on Left-to-Right layout text.
+        if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
+            drawerLayout.closeDrawer(GravityCompat.START);
         } else {
-            super.onBackPressed();
+            // Load the previous URL if available.
+            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 {
+                // Pass onBackPressed to the system.
+                super.onBackPressed();
+            }
         }
     }
 
+    @Override
+    public void onPause() {
+        // We need to pause the adView or it will continue to consume resources in the background on the free flavor.
+        BannerAd.pauseAd(adView);
+
+        super.onPause();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        // We need to resume the adView for the free flavor.
+        BannerAd.resumeAd(adView);
+    }
+
     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();
 
@@ -708,16 +1093,59 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateHome
 
             // Use the correct search URL based on javaScriptEnabled.
             if (javaScriptEnabled) {
-                formattedUrlString = "https://duckduckgo.com/?q=" + encodedUrlString;
-            } else {
-                formattedUrlString = "https://duckduckgo.com/html/?q=" + encodedUrlString;
+                formattedUrlString = javaScriptEnabledSearchURL + encodedUrlString;
+            } else { // JavaScript is disabled.
+                formattedUrlString = javaScriptDisabledSearchURL + encodedUrlString;
             }
         }
 
-        mainWebView.loadUrl(formattedUrlString);
+        mainWebView.loadUrl(formattedUrlString, customHeaders);
 
         // Hides the keyboard so we can see the webpage.
         InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
         inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
     }
+
+    public static void updatePrivacyIcons(Activity activity) {
+        // Get handles for the icons.
+        MenuItem privacyIcon = mainMenu.findItem(R.id.toggleJavaScript);
+        MenuItem firstPartyCookiesIcon = mainMenu.findItem(R.id.toggleFirstPartyCookies);
+        MenuItem domStorageIcon = mainMenu.findItem(R.id.toggleDomStorage);
+        MenuItem formDataIcon = mainMenu.findItem(R.id.toggleSaveFormData);
+
+        // Update `privacyIcon`.
+        if (javaScriptEnabled) {  // JavaScript is enabled.
+            privacyIcon.setIcon(R.drawable.javascript_enabled);
+        } else if (firstPartyCookiesEnabled) {  // JavaScript is disabled but cookies are enabled.
+            privacyIcon.setIcon(R.drawable.warning);
+        } else {  // All the dangerous features are disabled.
+            privacyIcon.setIcon(R.drawable.privacy_mode);
+        }
+
+        // Update `firstPartyCookiesIcon`.
+        if (firstPartyCookiesEnabled) {  // First-party cookies are enabled.
+            firstPartyCookiesIcon.setIcon(R.drawable.cookies_enabled);
+        } else {  // First-party cookies are disabled.
+            firstPartyCookiesIcon.setIcon(R.drawable.cookies_disabled);
+        }
+
+        // Update `domStorageIcon`.
+        if (javaScriptEnabled && domStorageEnabled) {  // Both JavaScript and DOM storage is enabled.
+            domStorageIcon.setIcon(R.drawable.dom_storage_enabled);
+        } else if (javaScriptEnabled){  // JavaScript is enabled but DOM storage is disabled.
+            domStorageIcon.setIcon(R.drawable.dom_storage_disabled);
+        } else {  // JavaScript is disabled, so DOM storage is ghosted.
+            domStorageIcon.setIcon(R.drawable.dom_storage_ghosted);
+        }
+
+        // Update `formDataIcon`.
+        if (saveFormDataEnabled) {  // Form data is enabled.
+            formDataIcon.setIcon(R.drawable.form_data_enabled);
+        } else {  // Form data is disabled.
+            formDataIcon.setIcon(R.drawable.form_data_disabled);
+        }
+
+        // `invalidateOptionsMenu` calls `onPrepareOptionsMenu()` and redraws the icons in the `AppBar`.
+        ActivityCompat.invalidateOptionsMenu(activity);
+    }
 }