]> 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 ca0cc89c5409fe5d0b81997598c8f3ba48813560..4433cf386ed82e30037aa660848c5950a5b2ee42 100644 (file)
@@ -30,11 +30,13 @@ 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.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;
@@ -51,6 +53,7 @@ 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;
@@ -65,76 +68,90 @@ 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 {
+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 SettingsFragment.
-    // It is also used in onCreate(), onOptionsItemSelected(), onNavigationItemSelected(), 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;
 
-    // formattedUrlString is public static so it can be accessed from BookmarksActivity.
-    // It is also used in onCreate(), onOptionsItemSelected(), onCreateHomeScreenShortcutCreate(), 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().
+    // `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().
+    // `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().
+    // `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().
+    // `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().
+    // `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;
 
+    // `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().
+    // `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;
 
-    // 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;
 
+    // `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")
@@ -142,6 +159,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         super.onCreate(savedInstanceState);
         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);
@@ -222,7 +242,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                     startActivity(emailIntent);
                     return true;
                 } else {  // Load the URL in Privacy Browser.
-                    mainWebView.loadUrl(url);
+                    mainWebView.loadUrl(url, customHeaders);
                     return true;
                 }
             }
@@ -243,6 +263,17 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                     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));
+            }
         });
 
         mainWebView.setWebChromeClient(new WebChromeClient() {
@@ -314,21 +345,11 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
 
         // 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.
-                requestUri.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
-
-                // Initiate the download and display a Snackbar.
-                downloadManager.enqueue(requestUri);
-                Snackbar.make(findViewById(R.id.mainWebView), R.string.download_started, Snackbar.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));
             }
         });
 
@@ -343,35 +364,35 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         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.  The default value is false.
-        javaScriptEnabled = savedPreferences.getBoolean("javascript_enabled", false);
+        javaScriptEnabled = sharedPreferences.getBoolean("javascript_enabled", false);
         mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
 
         // Initialize cookieManager.
         cookieManager = CookieManager.getInstance();
 
         // Set cookies initial status.  The default value is false.
-        firstPartyCookiesEnabled = savedPreferences.getBoolean("first_party_cookies_enabled", 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 = savedPreferences.getBoolean("third_party_cookies_enabled", false);
+            thirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies_enabled", false);
             cookieManager.setAcceptThirdPartyCookies(mainWebView, thirdPartyCookiesEnabled);
         }
 
         // Set DOM storage initial status.  The default value is false.
-        domStorageEnabled = savedPreferences.getBoolean("dom_storage_enabled", false);
+        domStorageEnabled = sharedPreferences.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);
+        saveFormDataEnabled = sharedPreferences.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");
+        String userAgentString = sharedPreferences.getString("user_agent", "Default user agent");
         switch (userAgentString) {
             case "Default user agent":
                 // Do nothing.
@@ -379,42 +400,55 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
 
             case "Custom user agent":
                 // Set the custom user agent on mainWebView,  The default is "PrivacyBrowser/1.0".
-                mainWebView.getSettings().setUserAgentString(savedPreferences.getString("custom_user_agent", "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(savedPreferences.getString("user_agent", "PrivacyBrowser/1.0"));
+                mainWebView.getSettings().setUserAgentString(sharedPreferences.getString("user_agent", "PrivacyBrowser/1.0"));
                 break;
         }
 
         // Set the initial string for JavaScript disabled search.
-        if (savedPreferences.getString("javascript_disabled_search", "https://duckduckgo.com/html/?q=").equals("Custom URL")) {
+        if (sharedPreferences.getString("javascript_disabled_search", "https://duckduckgo.com/html/?q=").equals("Custom URL")) {
             // Get the custom URL string.  The default is "".
-            javaScriptDisabledSearchURL = savedPreferences.getString("javascript_disabled_search_custom_url", "");
+            javaScriptDisabledSearchURL = sharedPreferences.getString("javascript_disabled_search_custom_url", "");
         } else {
             // Use the string from javascript_disabled_search.
-            javaScriptDisabledSearchURL = savedPreferences.getString("javascript_disabled_search", "https://duckduckgo.com/html/?q=");
+            javaScriptDisabledSearchURL = sharedPreferences.getString("javascript_disabled_search", "https://duckduckgo.com/html/?q=");
         }
 
         // Set the initial string for JavaScript enabled search.
-        if (savedPreferences.getString("javascript_enabled_search", "https://duckduckgo.com/?q=").equals("Custom URL")) {
+        if (sharedPreferences.getString("javascript_enabled_search", "https://duckduckgo.com/?q=").equals("Custom URL")) {
             // Get the custom URL string.  The default is "".
-            javaScriptEnabledSearchURL = savedPreferences.getString("javascript_enabled_search_custom_url", "");
+            javaScriptEnabledSearchURL = sharedPreferences.getString("javascript_enabled_search_custom_url", "");
         } else {
             // Use the string from javascript_enabled_search.
-            javaScriptEnabledSearchURL = savedPreferences.getString("javascript_enabled_search", "https://duckduckgo.com/?q=");
+            javaScriptEnabledSearchURL = sharedPreferences.getString("javascript_enabled_search", "https://duckduckgo.com/?q=");
         }
 
 
-        // Set homepage initial status.  The default value is "https://www.duckduckgo.com".
-        homepage = savedPreferences.getString("homepage", "https://www.duckduckgo.com");
+        // 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 swipe to refresh initial status.  The default is true.
-        swipeToRefreshEnabled = savedPreferences.getBoolean("swipe_to_refresh_enabled", true);
+        // 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");
+        }
+
+
         // Get the intent information that started the app.
         final Intent intent = getIntent();
 
@@ -430,9 +464,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         }
 
         // Load the initial website.
-        mainWebView.loadUrl(formattedUrlString);
+        mainWebView.loadUrl(formattedUrlString, customHeaders);
 
-        // Load the default favorite icon if it is null.
+        // 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);
@@ -448,7 +482,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) {
@@ -463,7 +497,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();
@@ -477,24 +511,32 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         // Set mainMenu so it can be used by onOptionsItemSelected.
         mainMenu = menu;
 
-        // Initialize privacyIcon
-        privacyIcon = menu.findItem(R.id.toggleJavaScript);
+        // Set the initial status of the privacy icon.
+        updatePrivacyIcons(privacyBrowserActivity);
 
-        // Get MenuItems for checkable menu items.
+        // 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);
         MenuItem toggleSaveFormData = menu.findItem(R.id.toggleSaveFormData);
 
-        // Set the initial status of the privacy icon.
-        updatePrivacyIcon();
-
         // Set the initial status of the menu item checkboxes.
         toggleFirstPartyCookies.setChecked(firstPartyCookiesEnabled);
         toggleThirdPartyCookies.setChecked(thirdPartyCookiesEnabled);
         toggleDomStorage.setChecked(domStorageEnabled);
         toggleSaveFormData.setChecked(saveFormDataEnabled);
 
+        // 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;
     }
 
@@ -521,6 +563,63 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         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;
+
+            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);
 
@@ -546,9 +645,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
 
                 // Update the privacy icon.
-                updatePrivacyIcon();
+                updatePrivacyIcons(privacyBrowserActivity);
 
-                // Display a Snackbar.
+                // Display a `Snackbar`.
                 if (javaScriptEnabled) {
                     Snackbar.make(findViewById(R.id.mainWebView), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
                 } else {
@@ -574,7 +673,18 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 cookieManager.setAcceptCookie(firstPartyCookiesEnabled);
 
                 // Update the privacy icon.
-                updatePrivacyIcon();
+                updatePrivacyIcons(privacyBrowserActivity);
+
+                // Display a `Snackbar`.
+                if (firstPartyCookiesEnabled) {
+                    Snackbar.make(findViewById(R.id.mainWebView), R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
+                } else {
+                    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();
@@ -591,6 +701,13 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                     // 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 {
+                        Snackbar.make(findViewById(R.id.mainWebView), R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
+                    }
+
                     // Reload the WebView.
                     mainWebView.reload();
                 } // Else do nothing because SDK < 21.
@@ -606,6 +723,13 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 // Apply the new DOM Storage status.
                 mainWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
 
+                // 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;
@@ -620,6 +744,13 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 // 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;
@@ -645,6 +776,34 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 mainWebView.reload();
                 return true;
 
+            case R.id.fontSizeFiftyPercent:
+                mainWebView.getSettings().setTextZoom(50);
+                return true;
+
+            case R.id.fontSizeSeventyFivePercent:
+                mainWebView.getSettings().setTextZoom(75);
+                return true;
+
+            case R.id.fontSizeOneHundredPercent:
+                mainWebView.getSettings().setTextZoom(100);
+                return true;
+
+            case R.id.fontSizeOneHundredTwentyFivePercent:
+                mainWebView.getSettings().setTextZoom(125);
+                return true;
+
+            case R.id.fontSizeOneHundredFiftyPercent:
+                mainWebView.getSettings().setTextZoom(150);
+                return true;
+
+            case R.id.fontSizeOneHundredSeventyFivePercent:
+                mainWebView.getSettings().setTextZoom(175);
+                return true;
+
+            case R.id.fontSizeTwoHundredPercent:
+                mainWebView.getSettings().setTextZoom(200);
+                return true;
+
             case R.id.share:
                 Intent shareIntent = new Intent();
                 shareIntent.setAction(Intent.ACTION_SEND);
@@ -654,11 +813,11 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 return true;
 
             case R.id.addToHomescreen:
-                // Show the CreateHomeScreenShortcut AlertDialog and name this instance "@string/create_shortcut".
+                // 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.
+                //Everything else will be handled by `CreateHomeScreenShortcut` and the associated listener below.
                 return true;
 
             case R.id.refresh:
@@ -679,7 +838,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:
@@ -750,8 +909,15 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 // 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();
 
@@ -789,11 +955,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
 
         // 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);
-    }
 
-    @Override
-    public void onCancelCreateHomeScreenShortcut(DialogFragment dialogFragment) {
-        // Do nothing because the user selected "Cancel".
+        // `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
@@ -815,6 +979,48 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         sendBroadcast(placeBookmarkShortcut);
     }
 
+    @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() {
@@ -893,22 +1099,53 @@ 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);
         inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
     }
 
-    private void updatePrivacyIcon() {
-        if (javaScriptEnabled) {
+    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) {
-                privacyIcon.setIcon(R.drawable.warning);
-            } else {
-                privacyIcon.setIcon(R.drawable.privacy_mode);
-            }
+        } 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);
     }
 }