]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
Fix swipe-to-refresh bug on switching tabs. https://redmine.stoutner.com/issues/643
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / MainWebViewActivity.java
index becbd5ac8ab5fd55ed65b6305196082fd87518f4..326a85706dc38df91e8941c6cebaedebd93e1d40 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2015-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2015-2021 Soren Stoutner <soren@stoutner.com>.
  *
  * Download cookie code contributed 2017 Hendrik Knackstedt.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
  *
@@ -31,6 +31,7 @@ import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ClipData;
 import android.content.ClipboardManager;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -46,6 +47,7 @@ import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.net.http.SslCertificate;
 import android.net.http.SslError;
+import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -102,11 +104,15 @@ import androidx.appcompat.widget.Toolbar;
 import androidx.coordinatorlayout.widget.CoordinatorLayout;
 import androidx.core.app.ActivityCompat;
 import androidx.core.content.ContextCompat;
+import androidx.core.content.FileProvider;
+import androidx.core.content.res.ResourcesCompat;
 import androidx.core.view.GravityCompat;
 import androidx.drawerlayout.widget.DrawerLayout;
 import androidx.fragment.app.DialogFragment;
 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
 import androidx.viewpager.widget.ViewPager;
+import androidx.webkit.WebSettingsCompat;
+import androidx.webkit.WebViewFeature;
 
 import com.google.android.material.appbar.AppBarLayout;
 import com.google.android.material.floatingactionbutton.FloatingActionButton;
@@ -132,7 +138,7 @@ import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog;
 import com.stoutner.privacybrowser.dialogs.OpenDialog;
 import com.stoutner.privacybrowser.dialogs.ProxyNotInstalledDialog;
 import com.stoutner.privacybrowser.dialogs.PinnedMismatchDialog;
-import com.stoutner.privacybrowser.dialogs.SaveDialog;
+import com.stoutner.privacybrowser.dialogs.SaveWebpageDialog;
 import com.stoutner.privacybrowser.dialogs.SslCertificateErrorDialog;
 import com.stoutner.privacybrowser.dialogs.StoragePermissionDialog;
 import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog;
@@ -142,7 +148,6 @@ import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
 import com.stoutner.privacybrowser.helpers.AdHelper;
 import com.stoutner.privacybrowser.helpers.BlocklistHelper;
 import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
-import com.stoutner.privacybrowser.helpers.CheckPinnedMismatchHelper;
 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
 import com.stoutner.privacybrowser.helpers.FileNameHelper;
 import com.stoutner.privacybrowser.helpers.ProxyHelper;
@@ -166,22 +171,23 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
         EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, OpenDialog.OpenListener,
-        PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveDialog.SaveWebpageListener, StoragePermissionDialog.StoragePermissionDialogListener,
+        PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveWebpageDialog.SaveWebpageListener, StoragePermissionDialog.StoragePermissionDialogListener,
         UrlHistoryDialog.NavigateHistoryListener, WebViewTabFragment.NewTabListener {
 
+    // The executor service handles background tasks.  It is accessed from `ViewSourceActivity`.
+    public static ExecutorService executorService = Executors.newFixedThreadPool(4);
+
     // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`.  It is also used in `onCreate()`, `onResume()`, and `applyProxy()`.
     public static String orbotStatus = "unknown";
 
     // The WebView pager adapter is accessed from `HttpAuthenticationDialog`, `PinnedMismatchDialog`, and `SslCertificateErrorDialog`.  It is also used in `onCreate()`, `onResume()`, and `addTab()`.
     public static WebViewPagerAdapter webViewPagerAdapter;
 
-    // The load URL on restart variables are public static so they can be accessed from `BookmarksActivity`.  They are used in `onRestart()`.
-    public static boolean loadUrlOnRestart;
-    public static String urlToLoadOnRestart;
-
     // `restartFromBookmarksActivity` is public static so it can be accessed from `BookmarksActivity`.  It is also used in `onRestart()`.
     public static boolean restartFromBookmarksActivity;
 
@@ -207,12 +213,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // It will be updated in `applyAppSettings()`, but it needs to be initialized here or the first run of `onPrepareOptionsMenu()` crashes.
     public static String proxyMode = ProxyHelper.NONE;
 
+    // Define the saved instance state constants.
+    private final String SAVED_STATE_ARRAY_LIST = "saved_state_array_list";
+    private final String SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST = "saved_nested_scroll_webview_state_array_list";
+    private final String SAVED_TAB_POSITION = "saved_tab_position";
+    private final String PROXY_MODE = "proxy_mode";
 
-    // The permission result request codes are used in `onCreateContextMenu()`, `onRequestPermissionResult()`, `onSaveWebpage()`, `onCloseStoragePermissionDialog()`, and `initializeWebView()`.
-    private final int PERMISSION_OPEN_REQUEST_CODE = 0;
-    private final int PERMISSION_SAVE_URL_REQUEST_CODE = 1;
-    private final int PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE = 2;
-    private final int PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE = 3;
+    // Define the saved instance state variables.
+    private ArrayList<Bundle> savedStateArrayList;
+    private ArrayList<Bundle> savedNestedScrollWebViewStateArrayList;
+    private int savedTabPosition;
+    private String savedProxyMode;
+
+    // Define the class variables.
+    @SuppressWarnings("rawtypes")
+    AsyncTask populateBlocklists;
 
     // The current WebView is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`,
     // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxy()`, and `applyDomainSettings()`.
@@ -224,9 +239,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // The search URL is set in `applyAppSettings()` and used in `onNewIntent()`, `loadUrlFromTextBox()`, `initializeApp()`, and `initializeWebView()`.
     private String searchURL;
 
-    // The options menu is set in `onCreateOptionsMenu()` and used in `onOptionsItemSelected()`, `updatePrivacyIcons()`, and `initializeWebView()`.
-    private Menu optionsMenu;
-
     // The blocklists are populated in `finishedPopulatingBlocklists()` and accessed from `initializeWebView()`.
     private ArrayList<List<String[]>> easyList;
     private ArrayList<List<String[]>> easyPrivacy;
@@ -308,6 +320,74 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     private String saveWebpageUrl;
     private String saveWebpageFilePath;
 
+    // Declare the class views.
+    private FrameLayout rootFrameLayout;
+    private DrawerLayout drawerLayout;
+    private RelativeLayout mainContentRelativeLayout;
+    private AppBarLayout appBarLayout;
+    private Toolbar toolbar;
+    private RelativeLayout urlRelativeLayout;
+    private EditText urlEditText;
+    private ActionBar actionBar;
+    private LinearLayout findOnPageLinearLayout;
+    private LinearLayout tabsLinearLayout;
+    private TabLayout tabLayout;
+    private SwipeRefreshLayout swipeRefreshLayout;
+    private ViewPager webViewPager;
+    private FrameLayout fullScreenVideoFrameLayout;
+
+    // Declare the class menus.
+    private Menu optionsMenu;
+
+    // Declare the class menu items.
+    private MenuItem navigationBackMenuItem;
+    private MenuItem navigationForwardMenuItem;
+    private MenuItem navigationHistoryMenuItem;
+    private MenuItem navigationRequestsMenuItem;
+    private MenuItem optionsPrivacyMenuItem;
+    private MenuItem optionsRefreshMenuItem;
+    private MenuItem optionsFirstPartyCookiesMenuItem;
+    private MenuItem optionsThirdPartyCookiesMenuItem;
+    private MenuItem optionsDomStorageMenuItem;
+    private MenuItem optionsSaveFormDataMenuItem;
+    private MenuItem optionsClearDataMenuItem;
+    private MenuItem optionsClearCookiesMenuItem;
+    private MenuItem optionsClearDomStorageMenuItem;
+    private MenuItem optionsClearFormDataMenuItem;
+    private MenuItem optionsBlocklistsMenuItem;
+    private MenuItem optionsEasyListMenuItem;
+    private MenuItem optionsEasyPrivacyMenuItem;
+    private MenuItem optionsFanboysAnnoyanceListMenuItem;
+    private MenuItem optionsFanboysSocialBlockingListMenuItem;
+    private MenuItem optionsUltraListMenuItem;
+    private MenuItem optionsUltraPrivacyMenuItem;
+    private MenuItem optionsBlockAllThirdPartyRequestsMenuItem;
+    private MenuItem optionsProxyMenuItem;
+    private MenuItem optionsProxyNoneMenuItem;
+    private MenuItem optionsProxyTorMenuItem;
+    private MenuItem optionsProxyI2pMenuItem;
+    private MenuItem optionsProxyCustomMenuItem;
+    private MenuItem optionsUserAgentMenuItem;
+    private MenuItem optionsUserAgentPrivacyBrowserMenuItem;
+    private MenuItem optionsUserAgentWebViewDefaultMenuItem;
+    private MenuItem optionsUserAgentFirefoxOnAndroidMenuItem;
+    private MenuItem optionsUserAgentChromeOnAndroidMenuItem;
+    private MenuItem optionsUserAgentSafariOnIosMenuItem;
+    private MenuItem optionsUserAgentFirefoxOnLinuxMenuItem;
+    private MenuItem optionsUserAgentChromiumOnLinuxMenuItem;
+    private MenuItem optionsUserAgentFirefoxOnWindowsMenuItem;
+    private MenuItem optionsUserAgentChromeOnWindowsMenuItem;
+    private MenuItem optionsUserAgentEdgeOnWindowsMenuItem;
+    private MenuItem optionsUserAgentInternetExplorerOnWindowsMenuItem;
+    private MenuItem optionsUserAgentSafariOnMacosMenuItem;
+    private MenuItem optionsUserAgentCustomMenuItem;
+    private MenuItem optionsSwipeToRefreshMenuItem;
+    private MenuItem optionsWideViewportMenuItem;
+    private MenuItem optionsDisplayImagesMenuItem;
+    private MenuItem optionsDarkWebViewMenuItem;
+    private MenuItem optionsFontSizeMenuItem;
+    private MenuItem optionsAddOrEditDomainMenuItem;
+
     @Override
     // Remove the warning about needing to override `performClick()` when using an `OnTouchListener` with `WebView`.
     @SuppressLint("ClickableViewAccessibility")
@@ -315,6 +395,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Run the default commands.
         super.onCreate(savedInstanceState);
 
+        // Check to see if the activity has been restarted.
+        if (savedInstanceState != null) {
+            // Store the saved instance state variables.
+            savedStateArrayList = savedInstanceState.getParcelableArrayList(SAVED_STATE_ARRAY_LIST);
+            savedNestedScrollWebViewStateArrayList = savedInstanceState.getParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST);
+            savedTabPosition = savedInstanceState.getInt(SAVED_TAB_POSITION);
+            savedProxyMode = savedInstanceState.getString(PROXY_MODE);
+        }
+
         // Initialize the default preference values the first time the program is run.  `false` keeps this command from resetting any current preferences back to default.
         PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
 
@@ -323,7 +412,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         // Get the screenshot preference.
         String appTheme = sharedPreferences.getString("app_theme", getString(R.string.app_theme_default_value));
-        boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
+        boolean allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false);
 
         // Get the theme entry values string array.
         String[] appThemeEntryValuesStringArray = getResources().getStringArray(R.array.app_theme_entry_values);
@@ -361,10 +450,33 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Set the content view.
         setContentView(R.layout.main_framelayout);
 
-        // Get handles for the views that need to be modified.
-        DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
-        Toolbar toolbar = findViewById(R.id.toolbar);
-        ViewPager webViewPager = findViewById(R.id.webviewpager);
+        // Get handles for the views.
+        rootFrameLayout = findViewById(R.id.root_framelayout);
+        drawerLayout = findViewById(R.id.drawerlayout);
+        mainContentRelativeLayout = findViewById(R.id.main_content_relativelayout);
+        appBarLayout = findViewById(R.id.appbar_layout);
+        toolbar = findViewById(R.id.toolbar);
+        findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
+        tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
+        tabLayout = findViewById(R.id.tablayout);
+        swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
+        webViewPager = findViewById(R.id.webviewpager);
+        fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout);
+
+        // Get a handle for the navigation view.
+        NavigationView navigationView = findViewById(R.id.navigationview);
+
+        // Get a handle for the navigation menu.
+        Menu navigationMenu = navigationView.getMenu();
+
+        // Get handles for the navigation menu items.
+        navigationBackMenuItem = navigationMenu.findItem(R.id.back);
+        navigationForwardMenuItem = navigationMenu.findItem(R.id.forward);
+        navigationHistoryMenuItem = navigationMenu.findItem(R.id.history);
+        navigationRequestsMenuItem = navigationMenu.findItem(R.id.requests);
+
+        // Listen for touches on the navigation menu.
+        navigationView.setNavigationItemSelectedListener(this);
 
         // Get a handle for the app compat delegate.
         AppCompatDelegate appCompatDelegate = getDelegate();
@@ -373,15 +485,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         appCompatDelegate.setSupportActionBar(toolbar);
 
         // Get a handle for the action bar.
-        ActionBar actionBar = appCompatDelegate.getSupportActionBar();
+        actionBar = appCompatDelegate.getSupportActionBar();
 
-        // This is needed to get rid of the Android Studio warning that the action bar might be null.
+        // Remove the incorrect lint warning below that the action bar might be null.
         assert actionBar != null;
 
         // Add the custom layout, which shows the URL text bar.
         actionBar.setCustomView(R.layout.url_app_bar);
         actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
 
+        // Get handles for the views in the URL app bar.
+        urlRelativeLayout = findViewById(R.id.url_relativelayout);
+        urlEditText = findViewById(R.id.url_edittext);
+
         // Create the hamburger icon at the start of the AppBar.
         actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.open_navigation_drawer, R.string.close_navigation_drawer);
 
@@ -397,8 +513,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Store up to 100 tabs in memory.
         webViewPager.setOffscreenPageLimit(100);
 
+        // Initialize the app.
+        initializeApp();
+
+        // Apply the app settings from the shared preferences.
+        applyAppSettings();
+
         // Populate the blocklists.
-        new PopulateBlocklists(this, this).execute();
+        populateBlocklists = new PopulateBlocklists(this, this).execute();
     }
 
     @Override
@@ -409,17 +531,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Replace the intent that started the app with this one.
         setIntent(intent);
 
-        // Process the intent here if Privacy Browser is fully initialized.  If the process has been killed by the system while sitting in the background, this will be handled in `initializeWebView()`.
-        if (ultraPrivacy != null) {
+        // Check to see if the app is being restarted from a saved state.
+        if (savedStateArrayList == null || savedStateArrayList.size() == 0) {  // The activity is not being restarted from a saved state.
             // Get the information from the intent.
             String intentAction = intent.getAction();
             Uri intentUriData = intent.getData();
+            String intentStringExtra = intent.getStringExtra(Intent.EXTRA_TEXT);
 
             // Determine if this is a web search.
             boolean isWebSearch = ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH));
 
             // Only process the URI if it contains data or it is a web search.  If the user pressed the desktop icon after the app was already running the URI will be null.
-            if (intentUriData != null || isWebSearch) {
+            if (intentUriData != null || intentStringExtra != null || isWebSearch) {
                 // Get the shared preferences.
                 SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
@@ -427,7 +550,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 String url;
 
                 // If the intent action is a web search, perform the search.
-                if (isWebSearch) {
+                if (isWebSearch) {  // The intent is a web search.
                     // Create an encoded URL string.
                     String encodedUrlString;
 
@@ -440,9 +563,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                     // Add the base search URL.
                     url = searchURL + encodedUrlString;
-                } else {  // The intent should contain a URL.
+                } else if (intentUriData != null) {  // The intent contains a URL formatted as a URI.
                     // Set the intent data as the URL.
                     url = intentUriData.toString();
+                } else {  // The intent contains a string, which might be a URL.
+                    // Set the intent string as the URL.
+                    url = intentStringExtra;
                 }
 
                 // Add a new tab if specified in the preferences.
@@ -457,9 +583,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     loadUrl(currentWebView, url);
                 }
 
-                // Get a handle for the drawer layout.
-                DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
-
                 // Close the navigation drawer if it is open.
                 if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
                     drawerLayout.closeDrawer(GravityCompat.START);
@@ -510,26 +633,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                     // Reapply the domain settings if the URL is not null, which can happen if an empty tab is active when returning from settings.
                     if (nestedScrollWebView.getUrl() != null) {
-                        applyDomainSettings(nestedScrollWebView, nestedScrollWebView.getUrl(), false, true);
+                        applyDomainSettings(nestedScrollWebView, nestedScrollWebView.getUrl(), false, true, false);
                     }
                 }
             }
         }
 
-        // Load the URL on restart (used when loading a bookmark).
-        if (loadUrlOnRestart) {
-            // Load the specified URL.
-            loadUrl(currentWebView, urlToLoadOnRestart);
-
-            // Reset the load on restart tracker.
-            loadUrlOnRestart = false;
-        }
-
         // Update the bookmarks drawer if returning from the Bookmarks activity.
         if (restartFromBookmarksActivity) {
-            // Get a handle for the drawer layout.
-            DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
-
             // Close the bookmarks drawer.
             drawerLayout.closeDrawer(GravityCompat.END);
 
@@ -544,11 +655,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         updatePrivacyIcons(true);
     }
 
-    // `onResume()` runs after `onStart()`, which runs after `onCreate()` and `onRestart()`.
+    // `onStart()` runs after `onCreate()` or `onRestart()`.  This is used instead of `onResume()` so the commands aren't called every time the screen is partially hidden.
     @Override
-    public void onResume() {
+    public void onStart() {
         // Run the default commands.
-        super.onResume();
+        super.onStart();
 
         // Resume any WebViews.
         for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
@@ -563,14 +674,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get the nested scroll WebView from the tab fragment.
                 NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
 
-                // Resume the nested scroll WebView JavaScript timers.
-                nestedScrollWebView.resumeTimers();
-
                 // Resume the nested scroll WebView.
                 nestedScrollWebView.onResume();
             }
         }
 
+        // Resume the nested scroll WebView JavaScript timers.  This is a global command that resumes JavaScript timers on all WebViews.
+        if (currentWebView != null) {
+            currentWebView.resumeTimers();
+        }
+
         // Reapply the proxy settings if the system is using a proxy.  This redisplays the appropriate alert dialog.
         if (!proxyMode.equals(ProxyHelper.NONE)) {
             applyProxy(false);
@@ -578,9 +691,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         // Reapply any system UI flags and the ad in the free flavor.
         if (displayingFullScreenVideo || inFullScreenBrowsingMode) {  // The system is displaying a website or a video in full screen mode.
-            // Get a handle for the root frame layouts.
-            FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
-
             /* Hide the system bars.
              * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
              * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
@@ -590,15 +700,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
                     View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
         } else if (BuildConfig.FLAVOR.contentEquals("free")) {  // The system in not in full screen mode.
+            // Get a handle for the ad view.  This cannot be a class variable because it changes with each ad load.
+            View adView = findViewById(R.id.adview);
+
             // Resume the ad.
-            AdHelper.resumeAd(findViewById(R.id.adview));
+            AdHelper.resumeAd(adView);
         }
     }
 
+    // `onStop()` runs after `onPause()`.  It is used instead of `onPause()` so the commands are not called every time the screen is partially hidden.
     @Override
-    public void onPause() {
+    public void onStop() {
         // Run the default commands.
-        super.onPause();
+        super.onStop();
 
         for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
             // Get the WebView tab fragment.
@@ -614,17 +728,66 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Pause the nested scroll WebView.
                 nestedScrollWebView.onPause();
-
-                // Pause the nested scroll WebView JavaScript timers.
-                nestedScrollWebView.pauseTimers();
             }
         }
 
+        // Pause the WebView JavaScript timers.  This is a global command that pauses JavaScript on all WebViews.
+        if (currentWebView != null) {
+            currentWebView.pauseTimers();
+        }
+
         // Pause the ad or it will continue to consume resources in the background on the free flavor.
         if (BuildConfig.FLAVOR.contentEquals("free")) {
+            // Get a handle for the ad view.  This cannot be a class variable because it changes with each ad load.
+            View adView = findViewById(R.id.adview);
+
             // Pause the ad.
-            AdHelper.pauseAd(findViewById(R.id.adview));
+            AdHelper.pauseAd(adView);
+        }
+    }
+
+    @Override
+    public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+        // Run the default commands.
+        super.onSaveInstanceState(savedInstanceState);
+
+        // Create the saved state array lists.
+        ArrayList<Bundle> savedStateArrayList = new ArrayList<>();
+        ArrayList<Bundle> savedNestedScrollWebViewStateArrayList = new ArrayList<>();
+
+        // Get the URLs from each tab.
+        for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+            // Get the WebView tab fragment.
+            WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
+
+            // Get the fragment view.
+            View fragmentView = webViewTabFragment.getView();
+
+            if (fragmentView != null) {
+                // Get the nested scroll WebView from the tab fragment.
+                NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+                // Create saved state bundle.
+                Bundle savedStateBundle = new Bundle();
+
+                // Get the current states.
+                nestedScrollWebView.saveState(savedStateBundle);
+                Bundle savedNestedScrollWebViewStateBundle = nestedScrollWebView.saveNestedScrollWebViewState();
+
+                // Store the saved states in the array lists.
+                savedStateArrayList.add(savedStateBundle);
+                savedNestedScrollWebViewStateArrayList.add(savedNestedScrollWebViewStateBundle);
+            }
         }
+
+        // Get the current tab position.
+        int currentTabPosition = tabLayout.getSelectedTabPosition();
+
+        // Store the saved states in the bundle.
+        savedInstanceState.putParcelableArrayList(SAVED_STATE_ARRAY_LIST, savedStateArrayList);
+        savedInstanceState.putParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST, savedNestedScrollWebViewStateArrayList);
+        savedInstanceState.putInt(SAVED_TAB_POSITION, currentTabPosition);
+        savedInstanceState.putString(PROXY_MODE, proxyMode);
     }
 
     @Override
@@ -644,6 +807,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             bookmarksDatabaseHelper.close();
         }
 
+        // Stop populating the blocklists if the AsyncTask is running in the background.
+        if (populateBlocklists != null) {
+            populateBlocklists.cancel(true);
+        }
+
         // Run the default commands.
         super.onDestroy();
     }
@@ -656,27 +824,70 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Store a handle for the options menu so it can be used by `onOptionsItemSelected()` and `updatePrivacyIcons()`.
         optionsMenu = menu;
 
+        // Get handles for the class menu items.
+        optionsPrivacyMenuItem = menu.findItem(R.id.javascript);
+        optionsRefreshMenuItem = menu.findItem(R.id.refresh);
+        optionsFirstPartyCookiesMenuItem = menu.findItem(R.id.first_party_cookies);
+        optionsThirdPartyCookiesMenuItem = menu.findItem(R.id.third_party_cookies);
+        optionsDomStorageMenuItem = menu.findItem(R.id.dom_storage);
+        optionsSaveFormDataMenuItem = menu.findItem(R.id.save_form_data);  // Form data can be removed once the minimum API >= 26.
+        optionsClearDataMenuItem = menu.findItem(R.id.clear_data);
+        optionsClearCookiesMenuItem = menu.findItem(R.id.clear_cookies);
+        optionsClearDomStorageMenuItem = menu.findItem(R.id.clear_dom_storage);
+        optionsClearFormDataMenuItem = menu.findItem(R.id.clear_form_data);  // Form data can be removed once the minimum API >= 26.
+        optionsBlocklistsMenuItem = menu.findItem(R.id.blocklists);
+        optionsEasyListMenuItem = menu.findItem(R.id.easylist);
+        optionsEasyPrivacyMenuItem = menu.findItem(R.id.easyprivacy);
+        optionsFanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list);
+        optionsFanboysSocialBlockingListMenuItem = menu.findItem(R.id.fanboys_social_blocking_list);
+        optionsUltraListMenuItem = menu.findItem(R.id.ultralist);
+        optionsUltraPrivacyMenuItem = menu.findItem(R.id.ultraprivacy);
+        optionsBlockAllThirdPartyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests);
+        optionsProxyMenuItem = menu.findItem(R.id.proxy);
+        optionsProxyNoneMenuItem = menu.findItem(R.id.proxy_none);
+        optionsProxyTorMenuItem = menu.findItem(R.id.proxy_tor);
+        optionsProxyI2pMenuItem = menu.findItem(R.id.proxy_i2p);
+        optionsProxyCustomMenuItem = menu.findItem(R.id.proxy_custom);
+        optionsUserAgentMenuItem = menu.findItem(R.id.user_agent);
+        optionsUserAgentPrivacyBrowserMenuItem = menu.findItem(R.id.user_agent_privacy_browser);
+        optionsUserAgentWebViewDefaultMenuItem = menu.findItem(R.id.user_agent_webview_default);
+        optionsUserAgentFirefoxOnAndroidMenuItem = menu.findItem(R.id.user_agent_firefox_on_android);
+        optionsUserAgentChromeOnAndroidMenuItem = menu.findItem(R.id.user_agent_chrome_on_android);
+        optionsUserAgentSafariOnIosMenuItem = menu.findItem(R.id.user_agent_safari_on_ios);
+        optionsUserAgentFirefoxOnLinuxMenuItem = menu.findItem(R.id.user_agent_firefox_on_linux);
+        optionsUserAgentChromiumOnLinuxMenuItem = menu.findItem(R.id.user_agent_chromium_on_linux);
+        optionsUserAgentFirefoxOnWindowsMenuItem = menu.findItem(R.id.user_agent_firefox_on_windows);
+        optionsUserAgentChromeOnWindowsMenuItem = menu.findItem(R.id.user_agent_chrome_on_windows);
+        optionsUserAgentEdgeOnWindowsMenuItem = menu.findItem(R.id.user_agent_edge_on_windows);
+        optionsUserAgentInternetExplorerOnWindowsMenuItem = menu.findItem(R.id.user_agent_internet_explorer_on_windows);
+        optionsUserAgentSafariOnMacosMenuItem = menu.findItem(R.id.user_agent_safari_on_macos);
+        optionsUserAgentCustomMenuItem = menu.findItem(R.id.user_agent_custom);
+        optionsSwipeToRefreshMenuItem = menu.findItem(R.id.swipe_to_refresh);
+        optionsWideViewportMenuItem = menu.findItem(R.id.wide_viewport);
+        optionsDisplayImagesMenuItem = menu.findItem(R.id.display_images);
+        optionsDarkWebViewMenuItem = menu.findItem(R.id.dark_webview);
+        optionsFontSizeMenuItem = menu.findItem(R.id.font_size);
+        optionsAddOrEditDomainMenuItem = menu.findItem(R.id.add_or_edit_domain);
+
+        // Get handles for the method menu items.
+        MenuItem bookmarksMenuItem = menu.findItem(R.id.bookmarks);
+        MenuItem adConsentMenuItem = menu.findItem(R.id.ad_consent);
+
         // Set the initial status of the privacy icons.  `false` does not call `invalidateOptionsMenu` as the last step.
         updatePrivacyIcons(false);
 
-        // Get handles for the menu items.
-        MenuItem toggleFirstPartyCookiesMenuItem = menu.findItem(R.id.toggle_first_party_cookies);
-        MenuItem toggleThirdPartyCookiesMenuItem = menu.findItem(R.id.toggle_third_party_cookies);
-        MenuItem toggleDomStorageMenuItem = menu.findItem(R.id.toggle_dom_storage);
-        MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data);  // Form data can be removed once the minimum API >= 26.
-        MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data);  // Form data can be removed once the minimum API >= 26.
-        MenuItem refreshMenuItem = menu.findItem(R.id.refresh);
-        MenuItem adConsentMenuItem = menu.findItem(R.id.ad_consent);
-
         // Only display third-party cookies if API >= 21
-        toggleThirdPartyCookiesMenuItem.setVisible(Build.VERSION.SDK_INT >= 21);
+        optionsThirdPartyCookiesMenuItem.setVisible(Build.VERSION.SDK_INT >= 21);
 
         // Only display the form data menu items if the API < 26.
-        toggleSaveFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26);
-        clearFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26);
+        optionsSaveFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26);
+        optionsClearFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26);
 
         // Disable the clear form data menu item if the API >= 26 so that the status of the main Clear Data is calculated correctly.
-        clearFormDataMenuItem.setEnabled(Build.VERSION.SDK_INT < 26);
+        optionsClearFormDataMenuItem.setEnabled(Build.VERSION.SDK_INT < 26);
+
+        // Only display the dark WebView menu item if API >= 21.
+        optionsDarkWebViewMenuItem.setVisible(Build.VERSION.SDK_INT >= 21);
 
         // Only show Ad Consent if this is the free flavor.
         adConsentMenuItem.setVisible(BuildConfig.FLAVOR.contentEquals("free"));
@@ -684,24 +895,24 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Get the shared preferences.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
-        // Get the dark theme and app bar preferences..
-        boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+        // Get the dark theme and app bar preferences.
+        boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean(getString(R.string.display_additional_app_bar_icons_key), false);
 
         // Set the status of the additional app bar icons.  Setting the refresh menu item to `SHOW_AS_ACTION_ALWAYS` makes it appear even on small devices like phones.
         if (displayAdditionalAppBarIcons) {
-            toggleFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
-            toggleDomStorageMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
-            refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+            optionsRefreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+            bookmarksMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            optionsFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
         } else { //Do not display the additional icons.
-            toggleFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-            toggleDomStorageMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-            refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+            optionsRefreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+            bookmarksMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+            optionsFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
         }
 
         // Replace Refresh with Stop if a URL is already loading.
         if (currentWebView != null && currentWebView.getProgress() != 100) {
             // Set the title.
-            refreshMenuItem.setTitle(R.string.stop);
+            optionsRefreshMenuItem.setTitle(R.string.stop);
 
             // Set the icon if it is displayed in the app bar.
             if (displayAdditionalAppBarIcons) {
@@ -709,12 +920,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
 
                 // Set the icon according to the current theme status.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    // Set the dark stop icon.
-                    refreshMenuItem.setIcon(R.drawable.close_night);
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+                    optionsRefreshMenuItem.setIcon(R.drawable.close_blue_day);
                 } else {
-                    // Set the light stop icon.
-                    refreshMenuItem.setIcon(R.drawable.close_day);
+                    optionsRefreshMenuItem.setIcon(R.drawable.close_blue_night);
                 }
             }
         }
@@ -725,32 +934,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
-        // Get handles for the menu items.
-        MenuItem addOrEditDomain = menu.findItem(R.id.add_or_edit_domain);
-        MenuItem firstPartyCookiesMenuItem = menu.findItem(R.id.toggle_first_party_cookies);
-        MenuItem thirdPartyCookiesMenuItem = menu.findItem(R.id.toggle_third_party_cookies);
-        MenuItem domStorageMenuItem = menu.findItem(R.id.toggle_dom_storage);
-        MenuItem saveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data);  // Form data can be removed once the minimum API >= 26.
-        MenuItem clearDataMenuItem = menu.findItem(R.id.clear_data);
-        MenuItem clearCookiesMenuItem = menu.findItem(R.id.clear_cookies);
-        MenuItem clearDOMStorageMenuItem = menu.findItem(R.id.clear_dom_storage);
-        MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data);  // Form data can be removed once the minimum API >= 26.
-        MenuItem blocklistsMenuItem = menu.findItem(R.id.blocklists);
-        MenuItem easyListMenuItem = menu.findItem(R.id.easylist);
-        MenuItem easyPrivacyMenuItem = menu.findItem(R.id.easyprivacy);
-        MenuItem fanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list);
-        MenuItem fanboysSocialBlockingListMenuItem = menu.findItem(R.id.fanboys_social_blocking_list);
-        MenuItem ultraListMenuItem = menu.findItem(R.id.ultralist);
-        MenuItem ultraPrivacyMenuItem = menu.findItem(R.id.ultraprivacy);
-        MenuItem blockAllThirdPartyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests);
-        MenuItem proxyMenuItem = menu.findItem(R.id.proxy);
-        MenuItem userAgentMenuItem = menu.findItem(R.id.user_agent);
-        MenuItem fontSizeMenuItem = menu.findItem(R.id.font_size);
-        MenuItem swipeToRefreshMenuItem = menu.findItem(R.id.swipe_to_refresh);
-        MenuItem wideViewportMenuItem = menu.findItem(R.id.wide_viewport);
-        MenuItem displayImagesMenuItem = menu.findItem(R.id.display_images);
-        MenuItem nightModeMenuItem = menu.findItem(R.id.night_mode);
-
         // Get a handle for the cookie manager.
         CookieManager cookieManager = CookieManager.getInstance();
 
@@ -762,9 +945,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         if (currentWebView != null) {
             // Set the add or edit domain text.
             if (currentWebView.getDomainSettingsApplied()) {
-                addOrEditDomain.setTitle(R.string.edit_domain_settings);
+                optionsAddOrEditDomainMenuItem.setTitle(R.string.edit_domain_settings);
             } else {
-                addOrEditDomain.setTitle(R.string.add_domain_settings);
+                optionsAddOrEditDomainMenuItem.setTitle(R.string.add_domain_settings);
             }
 
             // Get the current user agent from the WebView.
@@ -774,48 +957,52 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             fontSize = currentWebView.getSettings().getTextZoom();
 
             // Set the status of the menu item checkboxes.
-            domStorageMenuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
-            saveFormDataMenuItem.setChecked(currentWebView.getSettings().getSaveFormData());  // Form data can be removed once the minimum API >= 26.
-            easyListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
-            easyPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
-            fanboysAnnoyanceListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
-            fanboysSocialBlockingListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
-            ultraListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
-            ultraPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
-            blockAllThirdPartyRequestsMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
-            swipeToRefreshMenuItem.setChecked(currentWebView.getSwipeToRefresh());
-            wideViewportMenuItem.setChecked(currentWebView.getSettings().getUseWideViewPort());
-            displayImagesMenuItem.setChecked(currentWebView.getSettings().getLoadsImagesAutomatically());
-            nightModeMenuItem.setChecked(currentWebView.getNightMode());
+            optionsDomStorageMenuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
+            optionsSaveFormDataMenuItem.setChecked(currentWebView.getSettings().getSaveFormData());  // Form data can be removed once the minimum API >= 26.
+            optionsEasyListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
+            optionsEasyPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
+            optionsFanboysAnnoyanceListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
+            optionsFanboysSocialBlockingListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
+            optionsUltraListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
+            optionsUltraPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
+            optionsBlockAllThirdPartyRequestsMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
+            optionsSwipeToRefreshMenuItem.setChecked(currentWebView.getSwipeToRefresh());
+            optionsWideViewportMenuItem.setChecked(currentWebView.getSettings().getUseWideViewPort());
+            optionsDisplayImagesMenuItem.setChecked(currentWebView.getSettings().getLoadsImagesAutomatically());
 
             // Initialize the display names for the blocklists with the number of blocked requests.
-            blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + currentWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
-            easyListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASYLIST) + " - " + getString(R.string.easylist));
-            easyPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASYPRIVACY) + " - " + getString(R.string.easyprivacy));
-            fanboysAnnoyanceListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST) + " - " + getString(R.string.fanboys_annoyance_list));
-            fanboysSocialBlockingListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST) + " - " + getString(R.string.fanboys_social_blocking_list));
-            ultraListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.ULTRALIST) + " - " + getString(R.string.ultralist));
-            ultraPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.ULTRAPRIVACY) + " - " + getString(R.string.ultraprivacy));
-            blockAllThirdPartyRequestsMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.THIRD_PARTY_REQUESTS) + " - " + getString(R.string.block_all_third_party_requests));
+            optionsBlocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + currentWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+            optionsEasyListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASYLIST) + " - " + getString(R.string.easylist));
+            optionsEasyPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASYPRIVACY) + " - " + getString(R.string.easyprivacy));
+            optionsFanboysAnnoyanceListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST) + " - " + getString(R.string.fanboys_annoyance_list));
+            optionsFanboysSocialBlockingListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST) + " - " + getString(R.string.fanboys_social_blocking_list));
+            optionsUltraListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.ULTRALIST) + " - " + getString(R.string.ultralist));
+            optionsUltraPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.ULTRAPRIVACY) + " - " + getString(R.string.ultraprivacy));
+            optionsBlockAllThirdPartyRequestsMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.THIRD_PARTY_REQUESTS) + " - " + getString(R.string.block_all_third_party_requests));
 
             // Only modify third-party cookies if the API >= 21.
             if (Build.VERSION.SDK_INT >= 21) {
                 // Set the status of the third-party cookies checkbox.
-                thirdPartyCookiesMenuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView));
+                optionsThirdPartyCookiesMenuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView));
 
                 // Enable third-party cookies if first-party cookies are enabled.
-                thirdPartyCookiesMenuItem.setEnabled(cookieManager.acceptCookie());
+                optionsThirdPartyCookiesMenuItem.setEnabled(cookieManager.acceptCookie());
             }
 
             // Enable DOM Storage if JavaScript is enabled.
-            domStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled());
+            optionsDomStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled());
+
+            // Set the checkbox status for dark WebView if the WebView supports it.
+            if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+                optionsDarkWebViewMenuItem.setChecked(WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON);
+            }
         }
 
         // Set the checked status of the first party cookies menu item.
-        firstPartyCookiesMenuItem.setChecked(cookieManager.acceptCookie());
+        optionsFirstPartyCookiesMenuItem.setChecked(cookieManager.acceptCookie());
 
         // Enable Clear Cookies if there are any.
-        clearCookiesMenuItem.setEnabled(cookieManager.hasCookies());
+        optionsClearCookiesMenuItem.setEnabled(cookieManager.hasCookies());
 
         // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`, which links to `/data/data/com.stoutner.privacybrowser.standard`.
         String privateDataDirectoryString = getApplicationInfo().dataDir;
@@ -837,7 +1024,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         }
 
         // Enable Clear DOM Storage if there is any.
-        clearDOMStorageMenuItem.setEnabled(localStorageDirectoryNumberOfFiles > 0 || indexedDBDirectoryNumberOfFiles > 0);
+        optionsClearDomStorageMenuItem.setEnabled(localStorageDirectoryNumberOfFiles > 0 || indexedDBDirectoryNumberOfFiles > 0);
 
         // Enable Clear Form Data is there is any.  This can be removed once the minimum API >= 26.
         if (Build.VERSION.SDK_INT < 26) {
@@ -845,133 +1032,133 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(this);
 
             // Enable the clear form data menu item if there is anything to clear.
-            clearFormDataMenuItem.setEnabled(webViewDatabase.hasFormData());
+            optionsClearFormDataMenuItem.setEnabled(webViewDatabase.hasFormData());
         }
 
         // Enable Clear Data if any of the submenu items are enabled.
-        clearDataMenuItem.setEnabled(clearCookiesMenuItem.isEnabled() || clearDOMStorageMenuItem.isEnabled() || clearFormDataMenuItem.isEnabled());
+        optionsClearDataMenuItem.setEnabled(optionsClearCookiesMenuItem.isEnabled() || optionsClearDomStorageMenuItem.isEnabled() || optionsClearFormDataMenuItem.isEnabled());
 
         // Disable Fanboy's Social Blocking List menu item if Fanboy's Annoyance List is checked.
-        fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListMenuItem.isChecked());
+        optionsFanboysSocialBlockingListMenuItem.setEnabled(!optionsFanboysAnnoyanceListMenuItem.isChecked());
 
         // Set the proxy title and check the applied proxy.
         switch (proxyMode) {
             case ProxyHelper.NONE:
                 // Set the proxy title.
-                proxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_none));
+                optionsProxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_none));
 
                 // Check the proxy None radio button.
-                menu.findItem(R.id.proxy_none).setChecked(true);
+                optionsProxyNoneMenuItem.setChecked(true);
                 break;
 
             case ProxyHelper.TOR:
                 // Set the proxy title.
-                proxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_tor));
+                optionsProxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_tor));
 
                 // Check the proxy Tor radio button.
-                menu.findItem(R.id.proxy_tor).setChecked(true);
+                optionsProxyTorMenuItem.setChecked(true);
                 break;
 
             case ProxyHelper.I2P:
                 // Set the proxy title.
-                proxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_i2p));
+                optionsProxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_i2p));
 
                 // Check the proxy I2P radio button.
-                menu.findItem(R.id.proxy_i2p).setChecked(true);
+                optionsProxyI2pMenuItem.setChecked(true);
                 break;
 
             case ProxyHelper.CUSTOM:
                 // Set the proxy title.
-                proxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_custom));
+                optionsProxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_custom));
 
                 // Check the proxy Custom radio button.
-                menu.findItem(R.id.proxy_custom).setChecked(true);
+                optionsProxyCustomMenuItem.setChecked(true);
                 break;
         }
 
         // Select the current user agent menu item.  A switch statement cannot be used because the user agents are not compile time constants.
         if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[0])) {  // Privacy Browser.
             // Update the user agent menu item title.
-            userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_privacy_browser));
+            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_privacy_browser));
 
             // Select the Privacy Browser radio box.
-            menu.findItem(R.id.user_agent_privacy_browser).setChecked(true);
+            optionsUserAgentPrivacyBrowserMenuItem.setChecked(true);
         } else if (currentUserAgent.equals(webViewDefaultUserAgent)) {  // WebView Default.
             // Update the user agent menu item title.
-            userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_webview_default));
+            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_webview_default));
 
             // Select the WebView Default radio box.
-            menu.findItem(R.id.user_agent_webview_default).setChecked(true);
+            optionsUserAgentWebViewDefaultMenuItem.setChecked(true);
         } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[2])) {  // Firefox on Android.
             // Update the user agent menu item title.
-            userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_android));
+            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_android));
 
             // Select the Firefox on Android radio box.
-            menu.findItem(R.id.user_agent_firefox_on_android).setChecked(true);
+            optionsUserAgentFirefoxOnAndroidMenuItem.setChecked(true);
         } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[3])) {  // Chrome on Android.
             // Update the user agent menu item title.
-            userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chrome_on_android));
+            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chrome_on_android));
 
             // Select the Chrome on Android radio box.
-            menu.findItem(R.id.user_agent_chrome_on_android).setChecked(true);
+            optionsUserAgentChromeOnAndroidMenuItem.setChecked(true);
         } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[4])) {  // Safari on iOS.
             // Update the user agent menu item title.
-            userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_safari_on_ios));
+            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_safari_on_ios));
 
             // Select the Safari on iOS radio box.
-            menu.findItem(R.id.user_agent_safari_on_ios).setChecked(true);
+            optionsUserAgentSafariOnIosMenuItem.setChecked(true);
         } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[5])) {  // Firefox on Linux.
             // Update the user agent menu item title.
-            userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_linux));
+            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_linux));
 
             // Select the Firefox on Linux radio box.
-            menu.findItem(R.id.user_agent_firefox_on_linux).setChecked(true);
+            optionsUserAgentFirefoxOnLinuxMenuItem.setChecked(true);
         } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[6])) {  // Chromium on Linux.
             // Update the user agent menu item title.
-            userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chromium_on_linux));
+            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chromium_on_linux));
 
             // Select the Chromium on Linux radio box.
-            menu.findItem(R.id.user_agent_chromium_on_linux).setChecked(true);
+            optionsUserAgentChromiumOnLinuxMenuItem.setChecked(true);
         } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[7])) {  // Firefox on Windows.
             // Update the user agent menu item title.
-            userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_windows));
+            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_windows));
 
             // Select the Firefox on Windows radio box.
-            menu.findItem(R.id.user_agent_firefox_on_windows).setChecked(true);
+            optionsUserAgentFirefoxOnWindowsMenuItem.setChecked(true);
         } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[8])) {  // Chrome on Windows.
             // Update the user agent menu item title.
-            userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chrome_on_windows));
+            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chrome_on_windows));
 
             // Select the Chrome on Windows radio box.
-            menu.findItem(R.id.user_agent_chrome_on_windows).setChecked(true);
+            optionsUserAgentChromeOnWindowsMenuItem.setChecked(true);
         } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[9])) {  // Edge on Windows.
             // Update the user agent menu item title.
-            userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_edge_on_windows));
+            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_edge_on_windows));
 
             // Select the Edge on Windows radio box.
-            menu.findItem(R.id.user_agent_edge_on_windows).setChecked(true);
+            optionsUserAgentEdgeOnWindowsMenuItem.setChecked(true);
         } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[10])) {  // Internet Explorer on Windows.
             // Update the user agent menu item title.
-            userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_internet_explorer_on_windows));
+            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_internet_explorer_on_windows));
 
             // Select the Internet on Windows radio box.
-            menu.findItem(R.id.user_agent_internet_explorer_on_windows).setChecked(true);
+            optionsUserAgentInternetExplorerOnWindowsMenuItem.setChecked(true);
         } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[11])) {  // Safari on macOS.
             // Update the user agent menu item title.
-            userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_safari_on_macos));
+            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_safari_on_macos));
 
             // Select the Safari on macOS radio box.
-            menu.findItem(R.id.user_agent_safari_on_macos).setChecked(true);
+            optionsUserAgentSafariOnMacosMenuItem.setChecked(true);
         } else {  // Custom user agent.
             // Update the user agent menu item title.
-            userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_custom));
+            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_custom));
 
             // Select the Custom radio box.
-            menu.findItem(R.id.user_agent_custom).setChecked(true);
+            optionsUserAgentCustomMenuItem.setChecked(true);
         }
 
         // Set the font size title.
-        fontSizeMenuItem.setTitle(getString(R.string.font_size) + " - " + fontSize + "%");
+        optionsFontSizeMenuItem.setTitle(getString(R.string.font_size) + " - " + fontSize + "%");
 
         // Run all the other default commands.
         super.onPrepareOptionsMenu(menu);
@@ -981,1046 +1168,970 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     @Override
-    // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled.
-    @SuppressLint("SetJavaScriptEnabled")
     public boolean onOptionsItemSelected(MenuItem menuItem) {
-        // Get the selected menu item ID.
-        int menuItemId = menuItem.getItemId();
-
         // Get a handle for the shared preferences.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
         // Get a handle for the cookie manager.
         CookieManager cookieManager = CookieManager.getInstance();
 
+        // Get the selected menu item ID.
+        int menuItemId = menuItem.getItemId();
+
         // Run the commands that correlate to the selected menu item.
-        switch (menuItemId) {
-            case R.id.toggle_javascript:
-                // Toggle the JavaScript status.
-                currentWebView.getSettings().setJavaScriptEnabled(!currentWebView.getSettings().getJavaScriptEnabled());
-
-                // Update the privacy icon.  `true` runs `invalidateOptionsMenu` as the last step.
-                updatePrivacyIcons(true);
-
-                // Display a `Snackbar`.
-                if (currentWebView.getSettings().getJavaScriptEnabled()) {  // JavaScrip is enabled.
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
-                } else if (cookieManager.acceptCookie()) {  // JavaScript is disabled, but first-party cookies are enabled.
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
-                } else {  // Privacy mode.
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
-                }
+        if (menuItemId == R.id.javascript) {  // JavaScript.
+            // Toggle the JavaScript status.
+            currentWebView.getSettings().setJavaScriptEnabled(!currentWebView.getSettings().getJavaScriptEnabled());
 
+            // Update the privacy icon.
+            updatePrivacyIcons(true);
+
+            // Display a `Snackbar`.
+            if (currentWebView.getSettings().getJavaScriptEnabled()) {  // JavaScrip is enabled.
+                Snackbar.make(webViewPager, R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
+            } else if (cookieManager.acceptCookie()) {  // JavaScript is disabled, but first-party cookies are enabled.
+                Snackbar.make(webViewPager, R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
+            } else {  // Privacy mode.
+                Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
+            }
+
+            // Reload the current WebView.
+            currentWebView.reload();
+
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.refresh) {  // Refresh.
+            // Run the command that correlates to the current status of the menu item.
+            if (menuItem.getTitle().equals(getString(R.string.refresh))) {  // The refresh button was pushed.
                 // Reload the current WebView.
                 currentWebView.reload();
+            } else {  // The stop button was pushed.
+                // Stop the loading of the WebView.
+                currentWebView.stopLoading();
+            }
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.bookmarks) {  // Bookmarks.
+            // Open the bookmarks drawer.
+            drawerLayout.openDrawer(GravityCompat.END);
 
-            case R.id.refresh:
-                if (menuItem.getTitle().equals(getString(R.string.refresh))) {  // The refresh button was pushed.
-                    // Reload the current WebView.
-                    currentWebView.reload();
-                } else {  // The stop button was pushed.
-                    // Stop the loading of the WebView.
-                    currentWebView.stopLoading();
-                }
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.first_party_cookies) {  // First-party cookies.
+            // Switch the first-party cookie status.
+            cookieManager.setAcceptCookie(!cookieManager.acceptCookie());
 
-                // Consume the event.
-                return true;
+            // Store the first-party cookie status.
+            currentWebView.setAcceptFirstPartyCookies(cookieManager.acceptCookie());
 
-            case R.id.bookmarks:
-                // Get a handle for the drawer layout.
-                DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
+            // Update the menu checkbox.
+            menuItem.setChecked(cookieManager.acceptCookie());
 
-                // Open the bookmarks drawer.
-                drawerLayout.openDrawer(GravityCompat.END);
+            // Update the privacy icon.
+            updatePrivacyIcons(true);
 
-                // Consume the event.
-                return true;
+            // Display a snackbar.
+            if (cookieManager.acceptCookie()) {  // First-party cookies are enabled.
+                Snackbar.make(webViewPager, R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
+            } else if (currentWebView.getSettings().getJavaScriptEnabled()) {  // JavaScript is still enabled.
+                Snackbar.make(webViewPager, R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
+            } else {  // Privacy mode.
+                Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
+            }
 
-            case R.id.toggle_first_party_cookies:
-                // Switch the first-party cookie status.
-                cookieManager.setAcceptCookie(!cookieManager.acceptCookie());
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Store the first-party cookie status.
-                currentWebView.setAcceptFirstPartyCookies(cookieManager.acceptCookie());
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.third_party_cookies) {  // Third-party cookies.
+            // Only act if the API >= 21.  Otherwise, there are no third-party cookie controls.
+            if (Build.VERSION.SDK_INT >= 21) {
+                // Toggle the status of thirdPartyCookiesEnabled.
+                cookieManager.setAcceptThirdPartyCookies(currentWebView, !cookieManager.acceptThirdPartyCookies(currentWebView));
 
                 // Update the menu checkbox.
-                menuItem.setChecked(cookieManager.acceptCookie());
-
-                // Update the privacy icon.  `true` runs `invalidateOptionsMenu` as the last step.
-                updatePrivacyIcons(true);
+                menuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView));
 
                 // Display a snackbar.
-                if (cookieManager.acceptCookie()) {  // First-party cookies are enabled.
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
-                } else if (currentWebView.getSettings().getJavaScriptEnabled()) {  // JavaScript is still enabled.
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
-                } else {  // Privacy mode.
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
+                if (cookieManager.acceptThirdPartyCookies(currentWebView)) {
+                    Snackbar.make(webViewPager, R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
+                } else {
+                    Snackbar.make(webViewPager, R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
                 }
 
                 // Reload the current WebView.
                 currentWebView.reload();
+            }
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.dom_storage) {  // DOM storage.
+            // Toggle the status of domStorageEnabled.
+            currentWebView.getSettings().setDomStorageEnabled(!currentWebView.getSettings().getDomStorageEnabled());
 
-            case R.id.toggle_third_party_cookies:
-                if (Build.VERSION.SDK_INT >= 21) {
-                    // Switch the status of thirdPartyCookiesEnabled.
-                    cookieManager.setAcceptThirdPartyCookies(currentWebView, !cookieManager.acceptThirdPartyCookies(currentWebView));
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
 
-                    // Update the menu checkbox.
-                    menuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView));
+            // Update the privacy icon.
+            updatePrivacyIcons(true);
 
-                    // Display a snackbar.
-                    if (cookieManager.acceptThirdPartyCookies(currentWebView)) {
-                        Snackbar.make(findViewById(R.id.webviewpager), R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
-                    } else {
-                        Snackbar.make(findViewById(R.id.webviewpager), R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
-                    }
+            // Display a snackbar.
+            if (currentWebView.getSettings().getDomStorageEnabled()) {
+                Snackbar.make(webViewPager, R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show();
+            } else {
+                Snackbar.make(webViewPager, R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show();
+            }
 
-                    // Reload the current WebView.
-                    currentWebView.reload();
-                } // Else do nothing because SDK < 21.
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.save_form_data) {  // Form data.  This can be removed once the minimum API >= 26.
+            // Switch the status of saveFormDataEnabled.
+            currentWebView.getSettings().setSaveFormData(!currentWebView.getSettings().getSaveFormData());
 
-            case R.id.toggle_dom_storage:
-                // Toggle the status of domStorageEnabled.
-                currentWebView.getSettings().setDomStorageEnabled(!currentWebView.getSettings().getDomStorageEnabled());
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.getSettings().getSaveFormData());
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
+            // Display a snackbar.
+            if (currentWebView.getSettings().getSaveFormData()) {
+                Snackbar.make(webViewPager, R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show();
+            } else {
+                Snackbar.make(webViewPager, R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show();
+            }
 
-                // Update the privacy icon.  `true` refreshes the app bar icons.
-                updatePrivacyIcons(true);
+            // Update the privacy icon.
+            updatePrivacyIcons(true);
 
-                // Display a snackbar.
-                if (currentWebView.getSettings().getDomStorageEnabled()) {
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show();
-                } else {
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show();
-                }
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.clear_cookies) {  // Clear cookies.
+            // Create a snackbar.
+            Snackbar.make(webViewPager, R.string.cookies_deleted, Snackbar.LENGTH_LONG)
+                    .setAction(R.string.undo, v -> {
+                        // Do nothing because everything will be handled by `onDismissed()` below.
+                    })
+                    .addCallback(new Snackbar.Callback() {
+                        @Override
+                        public void onDismissed(Snackbar snackbar, int event) {
+                            if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
+                                // Delete the cookies, which command varies by SDK.
+                                if (Build.VERSION.SDK_INT < 21) {
+                                    cookieManager.removeAllCookie();
+                                } else {
+                                    cookieManager.removeAllCookies(null);
+                                }
+                            }
+                        }
+                    })
+                    .show();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.clear_dom_storage) {  // Clear DOM storage.
+            // Create a snackbar.
+            Snackbar.make(webViewPager, R.string.dom_storage_deleted, Snackbar.LENGTH_LONG)
+                    .setAction(R.string.undo, v -> {
+                        // Do nothing because everything will be handled by `onDismissed()` below.
+                    })
+                    .addCallback(new Snackbar.Callback() {
+                        @Override
+                        public void onDismissed(Snackbar snackbar, int event) {
+                            if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
+                                // Delete the DOM Storage.
+                                WebStorage webStorage = WebStorage.getInstance();
+                                webStorage.deleteAllData();
+
+                                // Initialize a handler to manually delete the DOM storage files and directories.
+                                Handler deleteDomStorageHandler = new Handler();
+
+                                // Setup a runnable to manually delete the DOM storage files and directories.
+                                Runnable deleteDomStorageRunnable = () -> {
+                                    try {
+                                        // Get a handle for the runtime.
+                                        Runtime runtime = Runtime.getRuntime();
+
+                                        // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`,
+                                        // which links to `/data/data/com.stoutner.privacybrowser.standard`.
+                                        String privateDataDirectoryString = getApplicationInfo().dataDir;
+
+                                        // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
+                                        Process deleteLocalStorageProcess = runtime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
+
+                                        // Multiple commands must be used because `Runtime.exec()` does not like `*`.
+                                        Process deleteIndexProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
+                                        Process deleteQuotaManagerProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
+                                        Process deleteQuotaManagerJournalProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
+                                        Process deleteDatabasesProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
+
+                                        // Wait for the processes to finish.
+                                        deleteLocalStorageProcess.waitFor();
+                                        deleteIndexProcess.waitFor();
+                                        deleteQuotaManagerProcess.waitFor();
+                                        deleteQuotaManagerJournalProcess.waitFor();
+                                        deleteDatabasesProcess.waitFor();
+                                    } catch (Exception exception) {
+                                        // Do nothing if an error is thrown.
+                                    }
+                                };
 
-            // Form data can be removed once the minimum API >= 26.
-            case R.id.toggle_save_form_data:
-                // Switch the status of saveFormDataEnabled.
-                currentWebView.getSettings().setSaveFormData(!currentWebView.getSettings().getSaveFormData());
+                                // Manually delete the DOM storage files after 200 milliseconds.
+                                deleteDomStorageHandler.postDelayed(deleteDomStorageRunnable, 200);
+                            }
+                        }
+                    })
+                    .show();
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.getSettings().getSaveFormData());
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.clear_form_data) {  // Clear form data.  This can be remove once the minimum API >= 26.
+            // Create a snackbar.
+            Snackbar.make(webViewPager, R.string.form_data_deleted, Snackbar.LENGTH_LONG)
+                    .setAction(R.string.undo, v -> {
+                        // Do nothing because everything will be handled by `onDismissed()` below.
+                    })
+                    .addCallback(new Snackbar.Callback() {
+                        @Override
+                        public void onDismissed(Snackbar snackbar, int event) {
+                            if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
+                                // Get a handle for the webView database.
+                                WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(getApplicationContext());
+
+                                // Delete the form data.
+                                webViewDatabase.clearFormData();
+                            }
+                        }
+                    })
+                    .show();
 
-                // Display a snackbar.
-                if (currentWebView.getSettings().getSaveFormData()) {
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show();
-                } else {
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show();
-                }
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.easylist) {  // EasyList.
+            // Toggle the EasyList status.
+            currentWebView.enableBlocklist(NestedScrollWebView.EASYLIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
 
-                // Update the privacy icon.  `true` runs `invalidateOptionsMenu` as the last step.
-                updatePrivacyIcons(true);
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.easyprivacy) {  // EasyPrivacy.
+            // Toggle the EasyPrivacy status.
+            currentWebView.enableBlocklist(NestedScrollWebView.EASYPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
 
-            case R.id.clear_cookies:
-                Snackbar.make(findViewById(R.id.webviewpager), R.string.cookies_deleted, Snackbar.LENGTH_LONG)
-                        .setAction(R.string.undo, v -> {
-                            // Do nothing because everything will be handled by `onDismissed()` below.
-                        })
-                        .addCallback(new Snackbar.Callback() {
-                            @SuppressLint("SwitchIntDef")  // Ignore the lint warning about not handling the other possible events as they are covered by `default:`.
-                            @Override
-                            public void onDismissed(Snackbar snackbar, int event) {
-                                if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
-                                    // Delete the cookies, which command varies by SDK.
-                                    if (Build.VERSION.SDK_INT < 21) {
-                                        cookieManager.removeAllCookie();
-                                    } else {
-                                        cookieManager.removeAllCookies(null);
-                                    }
-                                }
-                            }
-                        })
-                        .show();
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
 
-                // Consume the event.
-                return true;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-            case R.id.clear_dom_storage:
-                Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_deleted, Snackbar.LENGTH_LONG)
-                        .setAction(R.string.undo, v -> {
-                            // Do nothing because everything will be handled by `onDismissed()` below.
-                        })
-                        .addCallback(new Snackbar.Callback() {
-                            @SuppressLint("SwitchIntDef")  // Ignore the lint warning about not handling the other possible events as they are covered by `default:`.
-                            @Override
-                            public void onDismissed(Snackbar snackbar, int event) {
-                                if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
-                                    // Delete the DOM Storage.
-                                    WebStorage webStorage = WebStorage.getInstance();
-                                    webStorage.deleteAllData();
-
-                                    // Initialize a handler to manually delete the DOM storage files and directories.
-                                    Handler deleteDomStorageHandler = new Handler();
-
-                                    // Setup a runnable to manually delete the DOM storage files and directories.
-                                    Runnable deleteDomStorageRunnable = () -> {
-                                        try {
-                                            // Get a handle for the runtime.
-                                            Runtime runtime = Runtime.getRuntime();
-
-                                            // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`,
-                                            // which links to `/data/data/com.stoutner.privacybrowser.standard`.
-                                            String privateDataDirectoryString = getApplicationInfo().dataDir;
-
-                                            // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
-                                            Process deleteLocalStorageProcess = runtime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
-
-                                            // Multiple commands must be used because `Runtime.exec()` does not like `*`.
-                                            Process deleteIndexProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
-                                            Process deleteQuotaManagerProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
-                                            Process deleteQuotaManagerJournalProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
-                                            Process deleteDatabasesProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
-
-                                            // Wait for the processes to finish.
-                                            deleteLocalStorageProcess.waitFor();
-                                            deleteIndexProcess.waitFor();
-                                            deleteQuotaManagerProcess.waitFor();
-                                            deleteQuotaManagerJournalProcess.waitFor();
-                                            deleteDatabasesProcess.waitFor();
-                                        } catch (Exception exception) {
-                                            // Do nothing if an error is thrown.
-                                        }
-                                    };
-
-                                    // Manually delete the DOM storage files after 200 milliseconds.
-                                    deleteDomStorageHandler.postDelayed(deleteDomStorageRunnable, 200);
-                                }
-                            }
-                        })
-                        .show();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.fanboys_annoyance_list) {  // Fanboy's Annoyance List.
+            // Toggle Fanboy's Annoyance List status.
+            currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
 
-                // Consume the event.
-                return true;
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
 
-            // Form data can be remove once the minimum API >= 26.
-            case R.id.clear_form_data:
-                Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_deleted, Snackbar.LENGTH_LONG)
-                        .setAction(R.string.undo, v -> {
-                            // Do nothing because everything will be handled by `onDismissed()` below.
-                        })
-                        .addCallback(new Snackbar.Callback() {
-                            @SuppressLint("SwitchIntDef")  // Ignore the lint warning about not handling the other possible events as they are covered by `default:`.
-                            @Override
-                            public void onDismissed(Snackbar snackbar, int event) {
-                                if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
-                                    // Delete the form data.
-                                    WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(getApplicationContext());
-                                    mainWebViewDatabase.clearFormData();
-                                }
-                            }
-                        })
-                        .show();
+            // Update the staus of Fanboy's Social Blocking List.
+            optionsFanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
 
-                // Consume the event.
-                return true;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-            case R.id.easylist:
-                // Toggle the EasyList status.
-                currentWebView.enableBlocklist(NestedScrollWebView.EASYLIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.fanboys_social_blocking_list) {  // Fanboy's Social Blocking List.
+            // Toggle Fanboy's Social Blocking List status.
+            currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.ultralist) {  // UltraList.
+            // Toggle the UltraList status.
+            currentWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
 
-            case R.id.easyprivacy:
-                // Toggle the EasyPrivacy status.
-                currentWebView.enableBlocklist(NestedScrollWebView.EASYPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.ultraprivacy) {  // UltraPrivacy.
+            // Toggle the UltraPrivacy status.
+            currentWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
 
-                // Consume the event.
-                return true;
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
 
-            case R.id.fanboys_annoyance_list:
-                // Toggle Fanboy's Annoyance List status.
-                currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.block_all_third_party_requests) {  // Block all third-party requests.
+            //Toggle the third-party requests blocker status.
+            currentWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, !currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
 
-                // Update the staus of Fanboy's Social Blocking List.
-                MenuItem fanboysSocialBlockingListMenuItem = optionsMenu.findItem(R.id.fanboys_social_blocking_list);
-                fanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.proxy_none) {  // Proxy - None.
+            // Update the proxy mode.
+            proxyMode = ProxyHelper.NONE;
 
-            case R.id.fanboys_social_blocking_list:
-                // Toggle Fanboy's Social Blocking List status.
-                currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
+            // Apply the proxy mode.
+            applyProxy(true);
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.proxy_tor) {  // Proxy - Tor.
+            // Update the proxy mode.
+            proxyMode = ProxyHelper.TOR;
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Apply the proxy mode.
+            applyProxy(true);
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.proxy_i2p) {  // Proxy - I2P.
+            // Update the proxy mode.
+            proxyMode = ProxyHelper.I2P;
 
-            case R.id.ultralist:
-                // Toggle the UltraList status.
-                currentWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
+            // Apply the proxy mode.
+            applyProxy(true);
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.proxy_custom) {  // Proxy - Custom.
+            // Update the proxy mode.
+            proxyMode = ProxyHelper.CUSTOM;
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Apply the proxy mode.
+            applyProxy(true);
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_privacy_browser) {  // User Agent - Privacy Browser.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[0]);
 
-            case R.id.ultraprivacy:
-                // Toggle the UltraPrivacy status.
-                currentWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_webview_default) {  // User Agent - WebView Default.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString("");
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_firefox_on_android) {  // User Agent - Firefox on Android.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[2]);
 
-            case R.id.block_all_third_party_requests:
-                //Toggle the third-party requests blocker status.
-                currentWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, !currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_chrome_on_android) {  // User Agent - Chrome on Android.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[3]);
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_safari_on_ios) {  // User Agent - Safari on iOS.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[4]);
 
-            case R.id.proxy_none:
-                // Update the proxy mode.
-                proxyMode = ProxyHelper.NONE;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Apply the proxy mode.
-                applyProxy(true);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_firefox_on_linux) {  // User Agent - Firefox on Linux.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[5]);
 
-                // Consume the event.
-                return true;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-            case R.id.proxy_tor:
-                // Update the proxy mode.
-                proxyMode = ProxyHelper.TOR;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_chromium_on_linux) {  // User Agent - Chromium on Linux.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[6]);
 
-                // Apply the proxy mode.
-                applyProxy(true);
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_firefox_on_windows) {  // User Agent - Firefox on Windows.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[7]);
 
-            case R.id.proxy_i2p:
-                // Update the proxy mode.
-                proxyMode = ProxyHelper.I2P;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Apply the proxy mode.
-                applyProxy(true);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_chrome_on_windows) {  // User Agent - Chrome on Windows.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[8]);
 
-                // Consume the event.
-                return true;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-            case R.id.proxy_custom:
-                // Update the proxy mode.
-                proxyMode = ProxyHelper.CUSTOM;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_edge_on_windows) {  // User Agent - Edge on Windows.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[9]);
 
-                // Apply the proxy mode.
-                applyProxy(true);
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_privacy_browser:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[0]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_webview_default:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString("");
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_firefox_on_android:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[2]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_chrome_on_android:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[3]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_safari_on_ios:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[4]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_firefox_on_linux:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[5]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_chromium_on_linux:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[6]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_firefox_on_windows:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[7]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_chrome_on_windows:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[8]);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_internet_explorer_on_windows) {  // User Agent - Internet Explorer on Windows.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[10]);
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_safari_on_macos) {  // User Agent - Safari on macOS.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[11]);
 
-            case R.id.user_agent_edge_on_windows:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[9]);
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_custom) {  // User Agent - Custom.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)));
 
-            case R.id.user_agent_internet_explorer_on_windows:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[10]);
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.font_size) {  // Font size.
+            // Instantiate the font size dialog.
+            DialogFragment fontSizeDialogFragment = FontSizeDialog.displayDialog(currentWebView.getSettings().getTextZoom());
 
-                // Consume the event.
-                return true;
+            // Show the font size dialog.
+            fontSizeDialogFragment.show(getSupportFragmentManager(), getString(R.string.font_size));
 
-            case R.id.user_agent_safari_on_macos:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[11]);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.swipe_to_refresh) {  // Swipe to refresh.
+            // Toggle the stored status of swipe to refresh.
+            currentWebView.setSwipeToRefresh(!currentWebView.getSwipeToRefresh());
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Update the swipe refresh layout.
+            if (currentWebView.getSwipeToRefresh()) {  // Swipe to refresh is enabled.
+                // Only enable the swipe refresh layout if the WebView is scrolled to the top.  It is updated every time the scroll changes.
+                swipeRefreshLayout.setEnabled(currentWebView.getScrollY() == 0);
+            } else {  // Swipe to refresh is disabled.
+                // Disable the swipe refresh layout.
+                swipeRefreshLayout.setEnabled(false);
+            }
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.wide_viewport) {  // Wide viewport.
+            // Toggle the viewport.
+            currentWebView.getSettings().setUseWideViewPort(!currentWebView.getSettings().getUseWideViewPort());
 
-            case R.id.user_agent_custom:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.display_images) {  // Display images.
+            // Toggle the displaying of images.
+            if (currentWebView.getSettings().getLoadsImagesAutomatically()) {  // Images are currently loaded automatically.
+                // Disable loading of images.
+                currentWebView.getSettings().setLoadsImagesAutomatically(false);
 
-                // Reload the current WebView.
+                // Reload the website to remove existing images.
                 currentWebView.reload();
+            } else {  // Images are not currently loaded automatically.
+                // Enable loading of images.  Missing images will be loaded without the need for a reload.
+                currentWebView.getSettings().setLoadsImagesAutomatically(true);
+            }
 
-                // Consume the event.
-                return true;
-
-            case R.id.font_size:
-                // Instantiate the font size dialog.
-                DialogFragment fontSizeDialogFragment = FontSizeDialog.displayDialog(currentWebView.getSettings().getTextZoom());
-
-                // Show the font size dialog.
-                fontSizeDialogFragment.show(getSupportFragmentManager(), getString(R.string.font_size));
-
-                // Consume the event.
-                return true;
-
-            case R.id.swipe_to_refresh:
-                // Toggle the stored status of swipe to refresh.
-                currentWebView.setSwipeToRefresh(!currentWebView.getSwipeToRefresh());
-
-                // Get a handle for the swipe refresh layout.
-                SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
-
-                // Update the swipe refresh layout.
-                if (currentWebView.getSwipeToRefresh()) {  // Swipe to refresh is enabled.
-                    // Only enable the swipe refresh layout if the WebView is scrolled to the top.  It is updated every time the scroll changes.
-                    swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
-                } else {  // Swipe to refresh is disabled.
-                    // Disable the swipe refresh layout.
-                    swipeRefreshLayout.setEnabled(false);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.dark_webview) {  // Dark WebView.
+            // Check to see if dark WebView is supported by this WebView.
+            if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+                // Toggle the dark WebView setting.
+                if (WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON) {  // Dark WebView is currently enabled.
+                    // Turn off dark WebView.
+                    WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+                } else {  // Dark WebView is currently disabled.
+                    // Turn on dark WebView.
+                    WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
                 }
+            }
 
-                // Consume the event.
-                return true;
-
-            case R.id.wide_viewport:
-                // Toggle the viewport.
-                currentWebView.getSettings().setUseWideViewPort(!currentWebView.getSettings().getUseWideViewPort());
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.find_on_page) {  // Find on page.
+            // Get a handle for the views.
+            Toolbar toolbar = findViewById(R.id.toolbar);
+            LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
+            EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext);
 
-                // Consume the event.
-                return true;
+            // Set the minimum height of the find on page linear layout to match the toolbar.
+            findOnPageLinearLayout.setMinimumHeight(toolbar.getHeight());
 
-            case R.id.display_images:
-                if (currentWebView.getSettings().getLoadsImagesAutomatically()) {  // Images are currently loaded automatically.
-                    // Disable loading of images.
-                    currentWebView.getSettings().setLoadsImagesAutomatically(false);
+            // Hide the toolbar.
+            toolbar.setVisibility(View.GONE);
 
-                    // Reload the website to remove existing images.
-                    currentWebView.reload();
-                } else {  // Images are not currently loaded automatically.
-                    // Enable loading of images.  Missing images will be loaded without the need for a reload.
-                    currentWebView.getSettings().setLoadsImagesAutomatically(true);
-                }
+            // Show the find on page linear layout.
+            findOnPageLinearLayout.setVisibility(View.VISIBLE);
 
-                // Consume the event.
-                return true;
+            // Display the keyboard.  The app must wait 200 ms before running the command to work around a bug in Android.
+            // http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working
+            findOnPageEditText.postDelayed(() -> {
+                // Set the focus on the find on page edit text.
+                findOnPageEditText.requestFocus();
 
-            case R.id.night_mode:
-                // Toggle night mode.
-                currentWebView.setNightMode(!currentWebView.getNightMode());
-
-                // Enable or disable JavaScript according to night mode, the global preference, and any domain settings.
-                if (currentWebView.getNightMode()) {  // Night mode is enabled, which requires JavaScript.
-                    // Enable JavaScript.
-                    currentWebView.getSettings().setJavaScriptEnabled(true);
-                } else if (currentWebView.getDomainSettingsApplied()) {  // Night mode is disabled and domain settings are applied.  Set JavaScript according to the domain settings.
-                    // Apply the JavaScript preference that was stored the last time domain settings were loaded.
-                    currentWebView.getSettings().setJavaScriptEnabled(currentWebView.getDomainSettingsJavaScriptEnabled());
-                } else {  // Night mode is disabled and domain settings are not applied.  Set JavaScript according to the global preference.
-                    // Apply the JavaScript preference.
-                    currentWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false));
-                }
+                // Get a handle for the input method manager.
+                InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
 
-                // Update the privacy icons.
-                updatePrivacyIcons(false);
+                // Remove the lint warning below that the input method manager might be null.
+                assert inputMethodManager != null;
 
-                // Reload the website.
-                currentWebView.reload();
+                // Display the keyboard.  `0` sets no input flags.
+                inputMethodManager.showSoftInput(findOnPageEditText, 0);
+            }, 200);
 
-                // Consume the event.
-                return true;
-
-            case R.id.find_on_page:
-                // Get a handle for the views.
-                Toolbar toolbar = findViewById(R.id.toolbar);
-                LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
-                EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.print) {  // Print.
+            // Get a print manager instance.
+            PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
 
-                // Set the minimum height of the find on page linear layout to match the toolbar.
-                findOnPageLinearLayout.setMinimumHeight(toolbar.getHeight());
+            // Remove the lint error below that print manager might be null.
+            assert printManager != null;
 
-                // Hide the toolbar.
-                toolbar.setVisibility(View.GONE);
+            // Create a print document adapter from the current WebView.
+            PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter();
 
-                // Show the find on page linear layout.
-                findOnPageLinearLayout.setVisibility(View.VISIBLE);
+            // Print the document.
+            printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
 
-                // Display the keyboard.  The app must wait 200 ms before running the command to work around a bug in Android.
-                // http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working
-                findOnPageEditText.postDelayed(() -> {
-                    // Set the focus on `findOnPageEditText`.
-                    findOnPageEditText.requestFocus();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.save_url) {  // Save URL.
+            // Prepare the save dialog.  The dialog will be displayed once the file size and the content disposition have been acquired.
+            new PrepareSaveDialog(this, this, getSupportFragmentManager(), StoragePermissionDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(),
+                    currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl());
 
-                    // Get a handle for the input method manager.
-                    InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.save_archive) {  // Save archive.
+            // Instantiate the save dialog.
+            DialogFragment saveArchiveFragment = SaveWebpageDialog.saveWebpage(StoragePermissionDialog.SAVE_ARCHIVE, null, null, getString(R.string.webpage_mht), null,
+                    false);
 
-                    // Remove the lint warning below that the input method manager might be null.
-                    assert inputMethodManager != null;
+            // Show the save dialog.  It must be named `save_dialog` so that the file picker can update the file name.
+            saveArchiveFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
 
-                    // Display the keyboard.  `0` sets no input flags.
-                    inputMethodManager.showSoftInput(findOnPageEditText, 0);
-                }, 200);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.save_image) {  // Save image.
+            // Instantiate the save dialog.
+            DialogFragment saveImageFragment = SaveWebpageDialog.saveWebpage(StoragePermissionDialog.SAVE_IMAGE, null, null, getString(R.string.webpage_png), null,
+                    false);
 
-                // Consume the event.
-                return true;
+            // Show the save dialog.  It must be named `save_dialog` so that the file picker can update the file name.
+            saveImageFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
 
-            case R.id.print:
-                // Get a print manager instance.
-                PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.add_to_homescreen) {  // Add to homescreen.
+            // Instantiate the create home screen shortcut dialog.
+            DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), currentWebView.getUrl(),
+                    currentWebView.getFavoriteOrDefaultIcon());
 
-                // Remove the lint error below that print manager might be null.
-                assert printManager != null;
+            // Show the create home screen shortcut dialog.
+            createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut));
 
-                // Create a print document adapter from the current WebView.
-                PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.view_source) {  // View source.
+            // Create an intent to launch the view source activity.
+            Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
 
-                // Print the document.
-                printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
+            // Add the variables to the intent.
+            viewSourceIntent.putExtra(ViewSourceActivityKt.CURRENT_URL, currentWebView.getUrl());
+            viewSourceIntent.putExtra(ViewSourceActivityKt.USER_AGENT, currentWebView.getSettings().getUserAgentString());
 
-                // Consume the event.
-                return true;
+            // Make it so.
+            startActivity(viewSourceIntent);
 
-            case R.id.save_url:
-                // Prepare the save dialog.  The dialog will be displayed once the file size and the content disposition have been acquired.
-                new PrepareSaveDialog(this, this, getSupportFragmentManager(), StoragePermissionDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(),
-                        currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl());
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.share_url) {  // Share URL.
+            // Setup the share string.
+            String shareString = currentWebView.getTitle() + " – " + currentWebView.getUrl();
 
-                // Consume the event.
-                return true;
+            // Create the share intent.
+            Intent shareIntent = new Intent(Intent.ACTION_SEND);
 
-            case R.id.save_as_archive:
-                // Prepare the save dialog.  The dialog will be displayed once the file size and the content disposition have been acquired.
-                new PrepareSaveDialog(this, this, getSupportFragmentManager(), StoragePermissionDialog.SAVE_AS_ARCHIVE, currentWebView.getSettings().getUserAgentString(),
-                        currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl());
+            // Add the share string to the intent.
+            shareIntent.putExtra(Intent.EXTRA_TEXT, shareString);
 
-                // Consume the event.
-                return true;
+            // Set the MIME type.
+            shareIntent.setType("text/plain");
 
-            case R.id.save_as_image:
-                // Prepare the save dialog.  The dialog will be displayed once the file size adn the content disposition have been acquired.
-                new PrepareSaveDialog(this, this, getSupportFragmentManager(), StoragePermissionDialog.SAVE_AS_IMAGE, currentWebView.getSettings().getUserAgentString(),
-                        currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl());
+            // Set the intent to open in a new task.
+            shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-                // Consume the event.
-                return true;
+            // Make it so.
+            startActivity(Intent.createChooser(shareIntent, getString(R.string.share_url)));
 
-            case R.id.add_to_homescreen:
-                // Instantiate the create home screen shortcut dialog.
-                DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), currentWebView.getUrl(),
-                        currentWebView.getFavoriteOrDefaultIcon());
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.open_with_app) {  // Open with app.
+            // Open the URL with an outside app.
+            openWithApp(currentWebView.getUrl());
 
-                // Show the create home screen shortcut dialog.
-                createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.open_with_browser) {  // Open with browser.
+            // Open the URL with an outside browser.
+            openWithBrowser(currentWebView.getUrl());
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.add_or_edit_domain) {  // Add or edit domain.
+            // Check if domain settings currently exist.
+            if (currentWebView.getDomainSettingsApplied()) {  // Edit the current domain settings.
+                // Reapply the domain settings on returning to `MainWebViewActivity`.
+                reapplyDomainSettingsOnRestart = true;
 
-            case R.id.view_source:
-                // Create an intent to launch the view source activity.
-                Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
+                // Create an intent to launch the domains activity.
+                Intent domainsIntent = new Intent(this, DomainsActivity.class);
 
-                // Add the variables to the intent.
-                viewSourceIntent.putExtra("user_agent", currentWebView.getSettings().getUserAgentString());
-                viewSourceIntent.putExtra("current_url", currentWebView.getUrl());
+                // Add the extra information to the intent.
+                domainsIntent.putExtra("load_domain", currentWebView.getDomainSettingsDatabaseId());
+                domainsIntent.putExtra("close_on_back", true);
+                domainsIntent.putExtra("current_url", currentWebView.getUrl());
 
-                // Make it so.
-                startActivity(viewSourceIntent);
+                // Get the current certificate.
+                SslCertificate sslCertificate = currentWebView.getCertificate();
 
-                // Consume the event.
-                return true;
+                // Check to see if the SSL certificate is populated.
+                if (sslCertificate != null) {
+                    // Extract the certificate to strings.
+                    String issuedToCName = sslCertificate.getIssuedTo().getCName();
+                    String issuedToOName = sslCertificate.getIssuedTo().getOName();
+                    String issuedToUName = sslCertificate.getIssuedTo().getUName();
+                    String issuedByCName = sslCertificate.getIssuedBy().getCName();
+                    String issuedByOName = sslCertificate.getIssuedBy().getOName();
+                    String issuedByUName = sslCertificate.getIssuedBy().getUName();
+                    long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
+                    long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
 
-            case R.id.share_url:
-                // Setup the share string.
-                String shareString = currentWebView.getTitle() + " – " + currentWebView.getUrl();
+                    // Add the certificate to the intent.
+                    domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
+                    domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
+                    domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
+                    domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
+                    domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
+                    domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
+                    domainsIntent.putExtra("ssl_start_date", startDateLong);
+                    domainsIntent.putExtra("ssl_end_date", endDateLong);
+                }
 
-                // Create the share intent.
-                Intent shareIntent = new Intent(Intent.ACTION_SEND);
-                shareIntent.putExtra(Intent.EXTRA_TEXT, shareString);
-                shareIntent.setType("text/plain");
+                // Check to see if the current IP addresses have been received.
+                if (currentWebView.hasCurrentIpAddresses()) {
+                    // Add the current IP addresses to the intent.
+                    domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
+                }
 
                 // Make it so.
-                startActivity(Intent.createChooser(shareIntent, getString(R.string.share_url)));
-
-                // Consume the event.
-                return true;
-
-            case R.id.open_with_app:
-                // Open the URL with an outside app.
-                openWithApp(currentWebView.getUrl());
+                startActivity(domainsIntent);
+            } else {  // Add a new domain.
+                // Apply the new domain settings on returning to `MainWebViewActivity`.
+                reapplyDomainSettingsOnRestart = true;
 
-                // Consume the event.
-                return true;
+                // Get the current domain
+                Uri currentUri = Uri.parse(currentWebView.getUrl());
+                String currentDomain = currentUri.getHost();
 
-            case R.id.open_with_browser:
-                // Open the URL with an outside browser.
-                openWithBrowser(currentWebView.getUrl());
+                // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
+                DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0);
 
-                // Consume the event.
-                return true;
+                // Create the domain and store the database ID.
+                int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain);
 
-            case R.id.add_or_edit_domain:
-                if (currentWebView.getDomainSettingsApplied()) {  // Edit the current domain settings.
-                    // Reapply the domain settings on returning to `MainWebViewActivity`.
-                    reapplyDomainSettingsOnRestart = true;
-
-                    // Create an intent to launch the domains activity.
-                    Intent domainsIntent = new Intent(this, DomainsActivity.class);
-
-                    // Add the extra information to the intent.
-                    domainsIntent.putExtra("load_domain", currentWebView.getDomainSettingsDatabaseId());
-                    domainsIntent.putExtra("close_on_back", true);
-                    domainsIntent.putExtra("current_url", currentWebView.getUrl());
-
-                    // Get the current certificate.
-                    SslCertificate sslCertificate = currentWebView.getCertificate();
-
-                    // Check to see if the SSL certificate is populated.
-                    if (sslCertificate != null) {
-                        // Extract the certificate to strings.
-                        String issuedToCName = sslCertificate.getIssuedTo().getCName();
-                        String issuedToOName = sslCertificate.getIssuedTo().getOName();
-                        String issuedToUName = sslCertificate.getIssuedTo().getUName();
-                        String issuedByCName = sslCertificate.getIssuedBy().getCName();
-                        String issuedByOName = sslCertificate.getIssuedBy().getOName();
-                        String issuedByUName = sslCertificate.getIssuedBy().getUName();
-                        long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
-                        long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
-
-                        // Add the certificate to the intent.
-                        domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
-                        domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
-                        domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
-                        domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
-                        domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
-                        domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
-                        domainsIntent.putExtra("ssl_start_date", startDateLong);
-                        domainsIntent.putExtra("ssl_end_date", endDateLong);
-                    }
+                // Create an intent to launch the domains activity.
+                Intent domainsIntent = new Intent(this, DomainsActivity.class);
 
-                    // Check to see if the current IP addresses have been received.
-                    if (currentWebView.hasCurrentIpAddresses()) {
-                        // Add the current IP addresses to the intent.
-                        domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
-                    }
+                // Add the extra information to the intent.
+                domainsIntent.putExtra("load_domain", newDomainDatabaseId);
+                domainsIntent.putExtra("close_on_back", true);
+                domainsIntent.putExtra("current_url", currentWebView.getUrl());
 
-                    // Make it so.
-                    startActivity(domainsIntent);
-                } else {  // Add a new domain.
-                    // Apply the new domain settings on returning to `MainWebViewActivity`.
-                    reapplyDomainSettingsOnRestart = true;
-
-                    // Get the current domain
-                    Uri currentUri = Uri.parse(currentWebView.getUrl());
-                    String currentDomain = currentUri.getHost();
-
-                    // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
-                    DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0);
-
-                    // Create the domain and store the database ID.
-                    int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain);
-
-                    // Create an intent to launch the domains activity.
-                    Intent domainsIntent = new Intent(this, DomainsActivity.class);
-
-                    // Add the extra information to the intent.
-                    domainsIntent.putExtra("load_domain", newDomainDatabaseId);
-                    domainsIntent.putExtra("close_on_back", true);
-                    domainsIntent.putExtra("current_url", currentWebView.getUrl());
-
-                    // Get the current certificate.
-                    SslCertificate sslCertificate = currentWebView.getCertificate();
-
-                    // Check to see if the SSL certificate is populated.
-                    if (sslCertificate != null) {
-                        // Extract the certificate to strings.
-                        String issuedToCName = sslCertificate.getIssuedTo().getCName();
-                        String issuedToOName = sslCertificate.getIssuedTo().getOName();
-                        String issuedToUName = sslCertificate.getIssuedTo().getUName();
-                        String issuedByCName = sslCertificate.getIssuedBy().getCName();
-                        String issuedByOName = sslCertificate.getIssuedBy().getOName();
-                        String issuedByUName = sslCertificate.getIssuedBy().getUName();
-                        long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
-                        long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
-
-                        // Add the certificate to the intent.
-                        domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
-                        domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
-                        domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
-                        domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
-                        domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
-                        domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
-                        domainsIntent.putExtra("ssl_start_date", startDateLong);
-                        domainsIntent.putExtra("ssl_end_date", endDateLong);
-                    }
+                // Get the current certificate.
+                SslCertificate sslCertificate = currentWebView.getCertificate();
 
-                    // Check to see if the current IP addresses have been received.
-                    if (currentWebView.hasCurrentIpAddresses()) {
-                        // Add the current IP addresses to the intent.
-                        domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
-                    }
+                // Check to see if the SSL certificate is populated.
+                if (sslCertificate != null) {
+                    // Extract the certificate to strings.
+                    String issuedToCName = sslCertificate.getIssuedTo().getCName();
+                    String issuedToOName = sslCertificate.getIssuedTo().getOName();
+                    String issuedToUName = sslCertificate.getIssuedTo().getUName();
+                    String issuedByCName = sslCertificate.getIssuedBy().getCName();
+                    String issuedByOName = sslCertificate.getIssuedBy().getOName();
+                    String issuedByUName = sslCertificate.getIssuedBy().getUName();
+                    long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
+                    long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
 
-                    // Make it so.
-                    startActivity(domainsIntent);
+                    // Add the certificate to the intent.
+                    domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
+                    domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
+                    domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
+                    domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
+                    domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
+                    domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
+                    domainsIntent.putExtra("ssl_start_date", startDateLong);
+                    domainsIntent.putExtra("ssl_end_date", endDateLong);
                 }
 
-                // Consume the event.
-                return true;
+                // Check to see if the current IP addresses have been received.
+                if (currentWebView.hasCurrentIpAddresses()) {
+                    // Add the current IP addresses to the intent.
+                    domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
+                }
 
-            case R.id.ad_consent:
-                // Instantiate the ad consent dialog.
-                DialogFragment adConsentDialogFragment = new AdConsentDialog();
+                // Make it so.
+                startActivity(domainsIntent);
+            }
 
-                // Display the ad consent dialog.
-                adConsentDialogFragment.show(getSupportFragmentManager(), getString(R.string.ad_consent));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.ad_consent) {  // Ad consent.
+            // Instantiate the ad consent dialog.
+            DialogFragment adConsentDialogFragment = new AdConsentDialog();
 
-                // Consume the event.
-                return true;
+            // Display the ad consent dialog.
+            adConsentDialogFragment.show(getSupportFragmentManager(), getString(R.string.ad_consent));
 
-            default:
-                // Don't consume the event.
-                return super.onOptionsItemSelected(menuItem);
+            // Consume the event.
+            return true;
+        } else {  // There is no match with the options menu.  Pass the event up to the parent method.
+            // Don't consume the event.
+            return super.onOptionsItemSelected(menuItem);
         }
     }
 
     // removeAllCookies is deprecated, but it is required for API < 21.
     @Override
     public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
-        // Get the menu item ID.
-        int menuItemId = menuItem.getItemId();
-
         // Get a handle for the shared preferences.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
-        // Run the commands that correspond to the selected menu item.
-        switch (menuItemId) {
-            case R.id.clear_and_exit:
-                // Clear and exit Privacy Browser.
-                clearAndExit();
-                break;
-
-            case R.id.home:
-                // Load the homepage.
-                loadUrl(currentWebView, sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
-                break;
-
-            case R.id.back:
-                if (currentWebView.canGoBack()) {
-                    // Get the current web back forward list.
-                    WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
-
-                    // Get the previous entry URL.
-                    String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
-
-                    // Apply the domain settings.
-                    applyDomainSettings(currentWebView, previousUrl, false, false);
-
-                    // Load the previous website in the history.
-                    currentWebView.goBack();
-                }
-                break;
-
-            case R.id.forward:
-                if (currentWebView.canGoForward()) {
-                    // Get the current web back forward list.
-                    WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
-
-                    // Get the next entry URL.
-                    String nextUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() + 1).getUrl();
-
-                    // Apply the domain settings.
-                    applyDomainSettings(currentWebView, nextUrl, false, false);
-
-                    // Load the next website in the history.
-                    currentWebView.goForward();
-                }
-                break;
-
-            case R.id.history:
-                // Instantiate the URL history dialog.
-                DialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(currentWebView.getWebViewFragmentId());
-
-                // Show the URL history dialog.
-                urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history));
-                break;
-
-            case R.id.open:
-                // Instantiate the open file dialog.
-                DialogFragment openDialogFragment = new OpenDialog();
-
-                // Show the open file dialog.
-                openDialogFragment.show(getSupportFragmentManager(), getString(R.string.open));
-                break;
+        // Get the menu item ID.
+        int menuItemId = menuItem.getItemId();
 
-            case R.id.requests:
-                // Populate the resource requests.
-                RequestsActivity.resourceRequests = currentWebView.getResourceRequests();
+        // Run the commands that correspond to the selected menu item.
+        if (menuItemId == R.id.clear_and_exit) {  // Clear and exit.
+            // Clear and exit Privacy Browser.
+            clearAndExit();
+        } else if (menuItemId == R.id.home) {  // Home.
+            // Load the homepage.
+            loadUrl(currentWebView, sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
+        } else if (menuItemId == R.id.back) {  // Back.
+            // Check if the WebView can go back.
+            if (currentWebView.canGoBack()) {
+                // Get the current web back forward list.
+                WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
+
+                // Get the previous entry URL.
+                String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
 
-                // Create an intent to launch the Requests activity.
-                Intent requestsIntent = new Intent(this, RequestsActivity.class);
+                // Apply the domain settings.
+                applyDomainSettings(currentWebView, previousUrl, false, false, false);
 
-                // Add the block third-party requests status to the intent.
-                requestsIntent.putExtra("block_all_third_party_requests", currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
+                // Load the previous website in the history.
+                currentWebView.goBack();
+            }
+        } else if (menuItemId == R.id.forward) {  // Forward.
+            // Check if the WebView can go forward.
+            if (currentWebView.canGoForward()) {
+                // Get the current web back forward list.
+                WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
 
-                // Make it so.
-                startActivity(requestsIntent);
-                break;
+                // Get the next entry URL.
+                String nextUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() + 1).getUrl();
 
-            case R.id.downloads:
-                // Launch the system Download Manager.
-                Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
+                // Apply the domain settings.
+                applyDomainSettings(currentWebView, nextUrl, false, false, false);
 
-                // 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);
+                // Load the next website in the history.
+                currentWebView.goForward();
+            }
+        } else if (menuItemId == R.id.history) {  // History.
+            // Instantiate the URL history dialog.
+            DialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(currentWebView.getWebViewFragmentId());
 
-                // Make it so.
-                startActivity(downloadManagerIntent);
-                break;
+            // Show the URL history dialog.
+            urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history));
+        } else if (menuItemId == R.id.open) {  // Open.
+            // Instantiate the open file dialog.
+            DialogFragment openDialogFragment = new OpenDialog();
 
-            case R.id.domains:
-                // Set the flag to reapply the domain settings on restart when returning from Domain Settings.
-                reapplyDomainSettingsOnRestart = true;
+            // Show the open file dialog.
+            openDialogFragment.show(getSupportFragmentManager(), getString(R.string.open));
+        } else if (menuItemId == R.id.requests) {  // Requests.
+            // Populate the resource requests.
+            RequestsActivity.resourceRequests = currentWebView.getResourceRequests();
 
-                // Launch the domains activity.
-                Intent domainsIntent = new Intent(this, DomainsActivity.class);
+            // Create an intent to launch the Requests activity.
+            Intent requestsIntent = new Intent(this, RequestsActivity.class);
 
-                // Add the extra information to the intent.
-                domainsIntent.putExtra("current_url", currentWebView.getUrl());
+            // Add the block third-party requests status to the intent.
+            requestsIntent.putExtra("block_all_third_party_requests", currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
 
-                // Get the current certificate.
-                SslCertificate sslCertificate = currentWebView.getCertificate();
+            // Make it so.
+            startActivity(requestsIntent);
+        } else if (menuItemId == R.id.downloads) {  // Downloads.
+            // Launch the system Download Manager.
+            Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
 
-                // Check to see if the SSL certificate is populated.
-                if (sslCertificate != null) {
-                    // Extract the certificate to strings.
-                    String issuedToCName = sslCertificate.getIssuedTo().getCName();
-                    String issuedToOName = sslCertificate.getIssuedTo().getOName();
-                    String issuedToUName = sslCertificate.getIssuedTo().getUName();
-                    String issuedByCName = sslCertificate.getIssuedBy().getCName();
-                    String issuedByOName = sslCertificate.getIssuedBy().getOName();
-                    String issuedByUName = sslCertificate.getIssuedBy().getUName();
-                    long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
-                    long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
+            // 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);
 
-                    // Add the certificate to the intent.
-                    domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
-                    domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
-                    domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
-                    domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
-                    domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
-                    domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
-                    domainsIntent.putExtra("ssl_start_date", startDateLong);
-                    domainsIntent.putExtra("ssl_end_date", endDateLong);
-                }
+            // Make it so.
+            startActivity(downloadManagerIntent);
+        } else if (menuItemId == R.id.domains) {  // Domains.
+            // Set the flag to reapply the domain settings on restart when returning from Domain Settings.
+            reapplyDomainSettingsOnRestart = true;
 
-                // Check to see if the current IP addresses have been received.
-                if (currentWebView.hasCurrentIpAddresses()) {
-                    // Add the current IP addresses to the intent.
-                    domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
-                }
+            // Launch the domains activity.
+            Intent domainsIntent = new Intent(this, DomainsActivity.class);
 
-                // Make it so.
-                startActivity(domainsIntent);
-                break;
+            // Add the extra information to the intent.
+            domainsIntent.putExtra("current_url", currentWebView.getUrl());
+
+            // Get the current certificate.
+            SslCertificate sslCertificate = currentWebView.getCertificate();
+
+            // Check to see if the SSL certificate is populated.
+            if (sslCertificate != null) {
+                // Extract the certificate to strings.
+                String issuedToCName = sslCertificate.getIssuedTo().getCName();
+                String issuedToOName = sslCertificate.getIssuedTo().getOName();
+                String issuedToUName = sslCertificate.getIssuedTo().getUName();
+                String issuedByCName = sslCertificate.getIssuedBy().getCName();
+                String issuedByOName = sslCertificate.getIssuedBy().getOName();
+                String issuedByUName = sslCertificate.getIssuedBy().getUName();
+                long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
+                long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
+
+                // Add the certificate to the intent.
+                domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
+                domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
+                domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
+                domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
+                domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
+                domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
+                domainsIntent.putExtra("ssl_start_date", startDateLong);
+                domainsIntent.putExtra("ssl_end_date", endDateLong);
+            }
 
-            case R.id.settings:
-                // Set the flag to reapply app settings on restart when returning from Settings.
-                reapplyAppSettingsOnRestart = true;
+            // Check to see if the current IP addresses have been received.
+            if (currentWebView.hasCurrentIpAddresses()) {
+                // Add the current IP addresses to the intent.
+                domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
+            }
 
-                // Set the flag to reapply the domain settings on restart when returning from Settings.
-                reapplyDomainSettingsOnRestart = true;
+            // Make it so.
+            startActivity(domainsIntent);
+        } else if (menuItemId == R.id.settings) {  // Settings.
+            // Set the flag to reapply app settings on restart when returning from Settings.
+            reapplyAppSettingsOnRestart = true;
 
-                // Launch the settings activity.
-                Intent settingsIntent = new Intent(this, SettingsActivity.class);
-                startActivity(settingsIntent);
-                break;
+            // Set the flag to reapply the domain settings on restart when returning from Settings.
+            reapplyDomainSettingsOnRestart = true;
 
-            case R.id.import_export:
-                // Launch the import/export activity.
-                Intent importExportIntent = new Intent (this, ImportExportActivity.class);
-                startActivity(importExportIntent);
-                break;
+            // Launch the settings activity.
+            Intent settingsIntent = new Intent(this, SettingsActivity.class);
+            startActivity(settingsIntent);
+        } else if (menuItemId == R.id.import_export) { // Import/Export.
+            // Create an intent to launch the import/export activity.
+            Intent importExportIntent = new Intent(this, ImportExportActivity.class);
 
-            case R.id.logcat:
-                // Launch the logcat activity.
-                Intent logcatIntent = new Intent(this, LogcatActivity.class);
-                startActivity(logcatIntent);
-                break;
+            // Make it so.
+            startActivity(importExportIntent);
+        } else if (menuItemId == R.id.logcat) {  // Logcat.
+            // Create an intent to launch the logcat activity.
+            Intent logcatIntent = new Intent(this, LogcatActivity.class);
 
-            case R.id.guide:
-                // Launch `GuideActivity`.
-                Intent guideIntent = new Intent(this, GuideActivity.class);
-                startActivity(guideIntent);
-                break;
+            // Make it so.
+            startActivity(logcatIntent);
+        } else if (menuItemId == R.id.guide) {  // Guide.
+            // Create an intent to launch the guide activity.
+            Intent guideIntent = new Intent(this, GuideActivity.class);
 
-            case R.id.about:
-                // Create an intent to launch the about activity.
-                Intent aboutIntent = new Intent(this, AboutActivity.class);
+            // Make it so.
+            startActivity(guideIntent);
+        } else if (menuItemId == R.id.about) {  // About
+            // Create an intent to launch the about activity.
+            Intent aboutIntent = new Intent(this, AboutActivity.class);
 
-                // Create a string array for the blocklist versions.
-                String[] blocklistVersions = new String[] {easyList.get(0).get(0)[0], easyPrivacy.get(0).get(0)[0], fanboysAnnoyanceList.get(0).get(0)[0], fanboysSocialList.get(0).get(0)[0],
-                        ultraList.get(0).get(0)[0], ultraPrivacy.get(0).get(0)[0]};
+            // Create a string array for the blocklist versions.
+            String[] blocklistVersions = new String[]{easyList.get(0).get(0)[0], easyPrivacy.get(0).get(0)[0], fanboysAnnoyanceList.get(0).get(0)[0], fanboysSocialList.get(0).get(0)[0],
+                    ultraList.get(0).get(0)[0], ultraPrivacy.get(0).get(0)[0]};
 
-                // Add the blocklist versions to the intent.
-                aboutIntent.putExtra("blocklist_versions", blocklistVersions);
+            // Add the blocklist versions to the intent.
+            aboutIntent.putExtra("blocklist_versions", blocklistVersions);
 
-                // Make it so.
-                startActivity(aboutIntent);
-                break;
+            // Make it so.
+            startActivity(aboutIntent);
         }
 
-        // Get a handle for the drawer layout.
-        DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
-
         // Close the navigation drawer.
         drawerLayout.closeDrawer(GravityCompat.START);
         return true;
@@ -2042,8 +2153,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         // Reload the ad for the free flavor if not in full screen mode.
         if (BuildConfig.FLAVOR.contentEquals("free") && !inFullScreenBrowsingMode) {
+            // Get a handle for the ad view.  This cannot be a class variable because it changes with each ad load.
+            View adView = findViewById(R.id.adview);
+
             // Reload the ad.  The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
-            AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_unit_id));
+            // `getContext()` can be used instead of `getActivity.getApplicationContext()` once the minimum API >= 23.
+            AdHelper.loadAd(adView, getApplicationContext(), this, getString(R.string.ad_unit_id));
         }
 
         // `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:
@@ -2053,7 +2168,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     @Override
     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
-        // Store the hit test result.
+        // Get the hit test result.
         final WebView.HitTestResult hitTestResult = currentWebView.getHitTestResult();
 
         // Define the URL strings.
@@ -2141,8 +2256,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get the image URL.
                 imageUrl = hitTestResult.getExtra();
 
-                // Set the image URL as the title of the context menu.
-                menu.setHeaderTitle(imageUrl);
+                // Remove the incorrect lint warning below that the image URL might be null.
+                assert imageUrl != null;
+
+                // Set the context menu title.
+                if (imageUrl.startsWith("data:")) {  // The image data is contained in within the URL, making it exceedingly long.
+                    // Truncate the image URL before making it the title.
+                    menu.setHeaderTitle(imageUrl.substring(0, 100));
+                } else {  // The image URL does not contain the full image data.
+                    // Set the image URL as the title of the context menu.
+                    menu.setHeaderTitle(imageUrl);
+                }
 
                 // Add an Open in New Tab entry.
                 menu.add(R.string.open_image_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
@@ -2334,8 +2458,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // `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);
+                    try {
+                        // Make it so.
+                        startActivity(emailIntent);
+                    } catch (ActivityNotFoundException exception) {
+                        // Display a snackbar.
+                        Snackbar.make(currentWebView, getString(R.string.error) + "  " + exception, Snackbar.LENGTH_INDEFINITE).show();
+                    }
 
                     // Consume the event.
                     return true;
@@ -2469,14 +2598,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     @Override
-    public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, Bitmap favoriteIconBitmap) {
+    public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, @NonNull Bitmap favoriteIconBitmap) {
+        // Remove the incorrect lint warning below that the dialog fragment might be null.
+        assert dialogFragment != null;
+
         // Get the dialog.
         Dialog dialog = dialogFragment.getDialog();
 
         // Remove the incorrect lint warning below that the dialog might be null.
         assert dialog != null;
 
-        // Get handles for the views from `dialogFragment`.
+        // Get handles for the views from the dialog.
         EditText editFolderNameEditText = dialog.findViewById(R.id.edit_folder_name_edittext);
         RadioButton currentFolderIconRadioButton = dialog.findViewById(R.id.edit_folder_current_icon_radiobutton);
         RadioButton defaultFolderIconRadioButton = dialog.findViewById(R.id.edit_folder_default_icon_radiobutton);
@@ -2556,33 +2688,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         bookmarksCursorAdapter.changeCursor(bookmarksCursor);
     }
 
-    // Override `onBackPressed` to handle the navigation drawer and and the WebViews.
+    // Override `onBackPressed()` to handle the navigation drawer and and the WebViews.
     @Override
     public void onBackPressed() {
-        // Get a handle for the drawer layout and the tab layout.
-        DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
-        TabLayout tabLayout = findViewById(R.id.tablayout);
-
+        // Check the different options for processing `back`.
         if (drawerLayout.isDrawerVisible(GravityCompat.START)) {  // The navigation drawer is open.
             // Close the navigation drawer.
             drawerLayout.closeDrawer(GravityCompat.START);
         } else if (drawerLayout.isDrawerVisible(GravityCompat.END)){  // The bookmarks drawer is open.
-            if (currentBookmarksFolder.isEmpty()) {  // The home folder is displayed.
-                // close the bookmarks drawer.
-                drawerLayout.closeDrawer(GravityCompat.END);
-            } else {  // A subfolder is displayed.
-                // Place the former parent folder in `currentFolder`.
-                currentBookmarksFolder = bookmarksDatabaseHelper.getParentFolderName(currentBookmarksFolder);
-
-                // Load the new folder.
-                loadBookmarksFolder();
-            }
+            // close the bookmarks drawer.
+            drawerLayout.closeDrawer(GravityCompat.END);
         } else if (displayingFullScreenVideo) {  // A full screen video is shown.
-            // Get a handle for the layouts.
-            FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
-            RelativeLayout mainContentRelativeLayout = findViewById(R.id.main_content_relativelayout);
-            FrameLayout fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout);
-
             // Re-enable the screen timeout.
             fullScreenVideoFrameLayout.setKeepScreenOn(false);
 
@@ -2605,7 +2721,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) {  // Privacy Browser is currently in full screen browsing mode.
                 // Hide the banner ad in the free flavor.
                 if (BuildConfig.FLAVOR.contentEquals("free")) {
-                    AdHelper.hideAd(findViewById(R.id.adview));
+                    // Get a handle for the ad view.  This cannot be a class variable because it changes with each ad load.
+                    View adView = findViewById(R.id.adview);
+
+                    // Hide the banner ad.
+                    AdHelper.hideAd(adView);
                 }
 
                 /* Hide the system bars.
@@ -2629,8 +2749,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             // Reload the ad for the free flavor if not in full screen mode.
             if (BuildConfig.FLAVOR.contentEquals("free") && !inFullScreenBrowsingMode) {
-                // Reload the ad.
-                AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_unit_id));
+                // Get a handle for the ad view.  This cannot be a class variable because it changes with each ad load.
+                View adView = findViewById(R.id.adview);
+
+                // Reload the ad.  `getContext()` can be used instead of `getActivity.getApplicationContext()` once the minimum API >= 23.
+                AdHelper.loadAd(adView, getApplicationContext(), this, getString(R.string.ad_unit_id));
             }
         } else if (currentWebView.canGoBack()) {  // There is at least one item in the current WebView history.
             // Get the current web back forward list.
@@ -2640,7 +2763,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
 
             // Apply the domain settings.
-            applyDomainSettings(currentWebView, previousUrl, false, false);
+            applyDomainSettings(currentWebView, previousUrl, false, false, false);
 
             // Go back.
             currentWebView.goBack();
@@ -2753,9 +2876,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     private void loadUrlFromTextBox() {
-        // Get a handle for the URL edit text.
-        EditText urlEditText = findViewById(R.id.url_edittext);
-
         // 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 = urlEditText.getText().toString().trim();
 
@@ -2826,11 +2946,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Sanitize the URL.
         url = sanitizeUrl(url);
 
-        // Apply the domain settings.
-        applyDomainSettings(nestedScrollWebView, url, true, false);
-
-        // Load the URL.
-        nestedScrollWebView.loadUrl(url, customHeaders);
+        // Apply the domain settings and load the URL.
+        applyDomainSettings(nestedScrollWebView, url, true, false, true);
     }
 
     public void findPreviousOnPage(View view) {
@@ -2875,6 +2992,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     @Override
     public void onApplyNewFontSize(DialogFragment dialogFragment) {
+        // Remove the incorrect lint warning below that the dialog fragment might be null.
+        assert dialogFragment != null;
+
         // Get the dialog.
         Dialog dialog = dialogFragment.getDialog();
 
@@ -2912,6 +3032,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Get the file path string.
         openFilePath = fileNameEditText.getText().toString();
 
+        // Apply the domain settings.  This resets the favorite icon and removes any domain settings.
+        applyDomainSettings(currentWebView, "file://" + openFilePath, true, false, false);
+
         // Check to see if the storage permission is needed.
         if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {  // The storage permission has been granted.
             // Open the file.
@@ -2940,14 +3063,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission));
                 } else {  // Show the permission request directly.
                     // Request the write external storage permission.  The file will be opened when it finishes.
-                    ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_OPEN_REQUEST_CODE);
+                    ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, StoragePermissionDialog.OPEN);
                 }
             }
         }
     }
 
     @Override
-    public void onSaveWebpage(int saveType, DialogFragment dialogFragment) {
+    public void onSaveWebpage(int saveType, String originalUrlString, DialogFragment dialogFragment) {
         // Get the dialog.
         Dialog dialog = dialogFragment.getDialog();
 
@@ -2955,11 +3078,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         assert dialog != null;
 
         // Get a handle for the edit texts.
-        EditText urlEditText = dialog.findViewById(R.id.url_edittext);
+        EditText dialogUrlEditText = dialog.findViewById(R.id.url_edittext);
         EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext);
 
-        // Get the strings from the edit texts.
-        saveWebpageUrl = urlEditText.getText().toString();
+        // Store the URL.
+        if ((originalUrlString != null) && originalUrlString.startsWith("data:")) {
+            // Save the original URL.
+            saveWebpageUrl = originalUrlString;
+        } else {
+            // Get the URL from the edit text, which may have been modified.
+            saveWebpageUrl = dialogUrlEditText.getText().toString();
+        }
+
+        // Get the file path from the edit text.
         saveWebpageFilePath = fileNameEditText.getText().toString();
 
         // Check to see if the storage permission is needed.
@@ -2971,16 +3102,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
                     break;
 
-                case StoragePermissionDialog.SAVE_AS_ARCHIVE:
+                case StoragePermissionDialog.SAVE_ARCHIVE:
                     // Save the webpage archive.
-                    currentWebView.saveWebArchive(saveWebpageFilePath);
+                    saveWebpageArchive(saveWebpageFilePath);
                     break;
 
-                case StoragePermissionDialog.SAVE_AS_IMAGE:
+                case StoragePermissionDialog.SAVE_IMAGE:
                     // Save the webpage image.
-                    new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath);
+                    new SaveWebpageImage(this, this, saveWebpageFilePath, currentWebView).execute();
                     break;
             }
+
+            // Reset the strings.
+            saveWebpageUrl = "";
+            saveWebpageFilePath = "";
         } else {  // The storage permission has not been granted.
             // Get the external private directory file.
             File externalPrivateDirectoryFile = getExternalFilesDir(null);
@@ -3000,286 +3135,97 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
                         break;
 
-                    case StoragePermissionDialog.SAVE_AS_ARCHIVE:
+                    case StoragePermissionDialog.SAVE_ARCHIVE:
                         // Save the webpage archive.
-                        currentWebView.saveWebArchive(saveWebpageFilePath);
+                        saveWebpageArchive(saveWebpageFilePath);
                         break;
 
-                    case StoragePermissionDialog.SAVE_AS_IMAGE:
+                    case StoragePermissionDialog.SAVE_IMAGE:
                         // Save the webpage image.
-                        new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath);
+                        new SaveWebpageImage(this, this, saveWebpageFilePath, currentWebView).execute();
                         break;
                 }
+
+                // Reset the strings.
+                saveWebpageUrl = "";
+                saveWebpageFilePath = "";
             } else {  // The file path is in a public directory.
                 // Check if the user has previously denied the storage permission.
                 if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {  // Show a dialog explaining the request first.
                     // Instantiate the storage permission alert dialog.
                     DialogFragment storagePermissionDialogFragment = StoragePermissionDialog.displayDialog(saveType);
 
-                    // Show the storage permission alert dialog.  The permission will be requested when the dialog is closed.
-                    storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission));
-                } else {  // Show the permission request directly.
-                    switch (saveType) {
-                        case StoragePermissionDialog.SAVE_URL:
-                            // Request the write external storage permission.  The URL will be saved when it finishes.
-                            ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_URL_REQUEST_CODE);
-
-                        case StoragePermissionDialog.SAVE_AS_ARCHIVE:
-                            // Request the write external storage permission.  The webpage archive will be saved when it finishes.
-                            ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE);
-                            break;
-
-                        case StoragePermissionDialog.SAVE_AS_IMAGE:
-                            // Request the write external storage permission.  The webpage image will be saved when it finishes.
-                            ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE);
-                            break;
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
-    public void onCloseStoragePermissionDialog(int requestType) {
-        switch (requestType) {
-            case StoragePermissionDialog.OPEN:
-                // Request the write external storage permission.  The file will be opened when it finishes.
-                ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_OPEN_REQUEST_CODE);
-                break;
-
-            case StoragePermissionDialog.SAVE_URL:
-                // Request the write external storage permission.  The URL will be saved when it finishes.
-                ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_URL_REQUEST_CODE);
-                break;
-
-            case StoragePermissionDialog.SAVE_AS_ARCHIVE:
-                // Request the write external storage permission.  The webpage archive will be saved when it finishes.
-                ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE);
-                break;
-
-            case StoragePermissionDialog.SAVE_AS_IMAGE:
-                // Request the write external storage permission.  The webpage image will be saved when it finishes.
-                ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE);
-                break;
-        }
-    }
-
-    @Override
-    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
-        //Only process the results if they exist (this method is triggered when a dialog is presented the first time for an app, but no grant results are included).
-        if (grantResults.length > 0) {
-            switch (requestCode) {
-                case PERMISSION_OPEN_REQUEST_CODE:
-                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
-                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
-                        // Load the file.
-                        currentWebView.loadUrl("file://" + openFilePath);
-                    } else {  // The storage permission was not granted.
-                        // Display an error snackbar.
-                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
-                    }
-
-                    // Reset the open file path.
-                    openFilePath = "";
-                    break;
-
-                case PERMISSION_SAVE_URL_REQUEST_CODE:
-                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
-                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
-                        // Save the raw URL.
-                        new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
-                    } else {  // The storage permission was not granted.
-                        // Display an error snackbar.
-                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
-                    }
-
-                    // Reset the save strings.
-                    saveWebpageUrl = "";
-                    saveWebpageFilePath = "";
-                    break;
-
-                case PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE:
-                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
-                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
-                        // Save the webpage archive.
-                        currentWebView.saveWebArchive(saveWebpageFilePath);
-                    } else {  // The storage permission was not granted.
-                        // Display an error snackbar.
-                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
-                    }
-
-                    // Reset the save webpage file path.
-                    saveWebpageFilePath = "";
-                    break;
-
-                case PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE:
-                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
-                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
-                        // Save the webpage image.
-                        new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath);
-                    } else {  // The storage permission was not granted.
-                        // Display an error snackbar.
-                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
-                    }
-
-                    // Reset the save webpage file path.
-                    saveWebpageFilePath = "";
-                    break;
-            }
-        }
-    }
-
-    private void applyAppSettings() {
-        // Initialize the app if this is the first run.  This is done here instead of in `onCreate()` to shorten the time that an unthemed background is displayed on app startup.
-        if (webViewDefaultUserAgent == null) {
-            initializeApp();
-        }
-
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
-        // Store the values from the shared preferences in variables.
-        incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
-        boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
-        sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
-        sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
-        sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
-        proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value));
-        fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
-        hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true);
-        scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
-
-        // Get the search string.
-        String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value));
-
-        // Set the search string.
-        if (searchString.equals("Custom URL")) {  // A custom search string is used.
-            searchURL = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value));
-        } else {  // A custom search string is not used.
-            searchURL = searchString;
-        }
-
-        // Get a handle for the app compat delegate.
-        AppCompatDelegate appCompatDelegate = getDelegate();
-
-        // Get handles for the views that need to be modified.
-        FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
-        AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
-        ActionBar actionBar = appCompatDelegate.getSupportActionBar();
-        Toolbar toolbar = findViewById(R.id.toolbar);
-        LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
-        LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
-        SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
-
-        // Remove the incorrect lint warning below that the action bar might be null.
-        assert actionBar != null;
-
-        // Apply the proxy.
-        applyProxy(false);
-
-        // Set Do Not Track status.
-        if (doNotTrackEnabled) {
-            customHeaders.put("DNT", "1");
-        } else {
-            customHeaders.remove("DNT");
-        }
-
-        // Get the current layout parameters.  Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command.
-        CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
-        AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
-        AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams();
-        AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams();
-
-        // Add the scrolling behavior to the layout parameters.
-        if (scrollAppBar) {
-            // Enable scrolling of the app bar.
-            swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
-            toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
-            findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
-            tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
-        } else {
-            // Disable scrolling of the app bar.
-            swipeRefreshLayoutParams.setBehavior(null);
-            toolbarLayoutParams.setScrollFlags(0);
-            findOnPageLayoutParams.setScrollFlags(0);
-            tabsLayoutParams.setScrollFlags(0);
-
-            // Expand the app bar if it is currently collapsed.
-            appBarLayout.setExpanded(true);
-        }
-
-        // Apply the modified layout parameters.
-        swipeRefreshLayout.setLayoutParams(swipeRefreshLayoutParams);
-        toolbar.setLayoutParams(toolbarLayoutParams);
-        findOnPageLinearLayout.setLayoutParams(findOnPageLayoutParams);
-        tabsLinearLayout.setLayoutParams(tabsLayoutParams);
-
-        // Set the app bar scrolling for each WebView.
-        for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
-            // Get the WebView tab fragment.
-            WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
-
-            // Get the fragment view.
-            View fragmentView = webViewTabFragment.getView();
-
-            // Only modify the WebViews if they exist.
-            if (fragmentView != null) {
-                // Get the nested scroll WebView from the tab fragment.
-                NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
-
-                // Set the app bar scrolling.
-                nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar);
-            }
-        }
-
-        // Update the full screen browsing mode settings.
-        if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) {  // Privacy Browser is currently in full screen browsing mode.
-            // Update the visibility of the app bar, which might have changed in the settings.
-            if (hideAppBar) {
-                // Hide the tab linear layout.
-                tabsLinearLayout.setVisibility(View.GONE);
-
-                // Hide the action bar.
-                actionBar.hide();
-            } else {
-                // Show the tab linear layout.
-                tabsLinearLayout.setVisibility(View.VISIBLE);
-
-                // Show the action bar.
-                actionBar.show();
+                    // Show the storage permission alert dialog.  The permission will be requested when the dialog is closed.
+                    storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission));
+                } else {  // Show the permission request directly.
+                    // Request the write external storage permission according to the save type.  The URL will be saved when it finishes.
+                    ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, saveType);
+                }
             }
+        }
+    }
 
-            // Hide the banner ad in the free flavor.
-            if (BuildConfig.FLAVOR.contentEquals("free")) {
-                AdHelper.hideAd(findViewById(R.id.adview));
-            }
+    @Override
+    public void onCloseStoragePermissionDialog(int requestType) {
+        // Request the write external storage permission according to the request type.  The file will be opened when it finishes.
+        ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestType);
 
-            /* Hide the system bars.
-             * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
-             * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
-             * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
-             * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
-             */
-            rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
-                    View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
-        } else {  // Privacy Browser is not in full screen browsing mode.
-            // Reset the full screen tracker, which could be true if Privacy Browser was in full screen mode before entering settings and full screen browsing was disabled.
-            inFullScreenBrowsingMode = false;
+    }
 
-            // Show the tab linear layout.
-            tabsLinearLayout.setVisibility(View.VISIBLE);
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        //Only process the results if they exist (this method is triggered when a dialog is presented the first time for an app, but no grant results are included).
+        if (grantResults.length > 0) {
+            switch (requestCode) {
+                case StoragePermissionDialog.OPEN:
+                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
+                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
+                        // Load the file.
+                        currentWebView.loadUrl("file://" + openFilePath);
+                    } else {  // The storage permission was not granted.
+                        // Display an error snackbar.
+                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+                    }
+                    break;
 
-            // Show the action bar.
-            actionBar.show();
+                case StoragePermissionDialog.SAVE_URL:
+                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
+                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
+                        // Save the raw URL.
+                        new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
+                    } else {  // The storage permission was not granted.
+                        // Display an error snackbar.
+                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+                    }
+                    break;
 
-            // Show the banner ad in the free flavor.
-            if (BuildConfig.FLAVOR.contentEquals("free")) {
-                // Initialize the ads.  If this isn't the first run, `loadAd()` will be automatically called instead.
-                AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getSupportFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id));
+                case StoragePermissionDialog.SAVE_ARCHIVE:
+                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
+                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
+                        // Save the webpage archive.
+                        saveWebpageArchive(saveWebpageFilePath);
+                    } else {  // The storage permission was not granted.
+                        // Display an error snackbar.
+                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+                    }
+                    break;
+
+                case StoragePermissionDialog.SAVE_IMAGE:
+                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
+                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
+                        // Save the webpage image.
+                        new SaveWebpageImage(this, this, saveWebpageFilePath, currentWebView).execute();
+                    } else {  // The storage permission was not granted.
+                        // Display an error snackbar.
+                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+                    }
+                    break;
             }
 
-            // Remove the `SYSTEM_UI` flags from the root frame layout.
-            rootFrameLayout.setSystemUiVisibility(0);
+            // Reset the strings.
+            openFilePath = "";
+            saveWebpageUrl = "";
+            saveWebpageFilePath = "";
         }
     }
 
@@ -3290,13 +3236,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Remove the lint warning below that the input method manager might be null.
         assert inputMethodManager != null;
 
-        // Initialize the foreground color spans for highlighting the URLs.  We have to use the deprecated `getColor()` until API >= 23.
-        redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+        // Initialize the gray foreground color spans for highlighting the URLs.  The deprecated `getResources()` must be used until API >= 23.
         initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
         finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
 
-        // Get handles for the URL views.
-        EditText urlEditText = findViewById(R.id.url_edittext);
+        // Get the current theme status.
+        int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+        // Set the red color span according to the theme.
+        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+            redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+        } else {
+            redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_900));
+        }
 
         // Remove the formatting from the URL edit text when the user is editing the text.
         urlEditText.setOnFocusChangeListener((View v, boolean hasFocus) -> {
@@ -3386,27 +3338,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         this.registerReceiver(orbotStatusBroadcastReceiver, new IntentFilter("org.torproject.android.intent.action.STATUS"));
 
         // Get handles for views that need to be modified.
-        DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
-        NavigationView navigationView = findViewById(R.id.navigationview);
-        TabLayout tabLayout = findViewById(R.id.tablayout);
-        SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
-        ViewPager webViewPager = findViewById(R.id.webviewpager);
         ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
         FloatingActionButton launchBookmarksActivityFab = findViewById(R.id.launch_bookmarks_activity_fab);
         FloatingActionButton createBookmarkFolderFab = findViewById(R.id.create_bookmark_folder_fab);
         FloatingActionButton createBookmarkFab = findViewById(R.id.create_bookmark_fab);
         EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext);
 
-        // Listen for touches on the navigation menu.
-        navigationView.setNavigationItemSelectedListener(this);
-
-        // Get handles for the navigation menu and the back and forward menu items.  The menu is 0 based.
-        Menu navigationMenu = navigationView.getMenu();
-        MenuItem navigationBackMenuItem = navigationMenu.getItem(2);
-        MenuItem navigationForwardMenuItem = navigationMenu.getItem(3);
-        MenuItem navigationHistoryMenuItem = navigationMenu.getItem(4);
-        MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
-
         // Update the web view pager every time a tab is modified.
         webViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
             @Override
@@ -3422,13 +3359,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Set the current WebView.
                 setCurrentWebView(position);
 
-                // Select the corresponding tab if it does not match the currently selected page.  This will happen if the page was scrolled via swiping in the view pager or by creating a new tab.
+                // Select the corresponding tab if it does not match the currently selected page.  This will happen if the page was scrolled by creating a new tab.
                 if (tabLayout.getSelectedTabPosition() != position) {
-                    // Create a handler to select the tab.
-                    Handler selectTabHandler = new Handler();
-
-                    // Create a runnable to select the tab.
-                    Runnable selectTabRunnable = () -> {
+                    // Wait until the new tab has been created.
+                    tabLayout.post(() -> {
                         // Get a handle for the tab.
                         TabLayout.Tab tab = tabLayout.getTabAt(position);
 
@@ -3437,10 +3371,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                         // Select the tab.
                         tab.select();
-                    };
-
-                    // Select the tab layout after 150 milliseconds, which leaves enough time for a new tab to be inflated.
-                    selectTabHandler.postDelayed(selectTabRunnable, 150);
+                    });
                 }
             }
 
@@ -3560,14 +3491,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         defaultProgressViewStartOffset = swipeRefreshLayout.getProgressViewStartOffset();
         defaultProgressViewEndOffset = swipeRefreshLayout.getProgressViewEndOffset();
 
-        // Get the current theme status.
-        int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
         // Set the refresh color scheme according to the theme.
-        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-            swipeRefreshLayout.setColorSchemeResources(R.color.blue_500);
-        } else {
+        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
             swipeRefreshLayout.setColorSchemeResources(R.color.blue_700);
+        } else {
+            swipeRefreshLayout.setColorSchemeResources(R.color.violet_500);
         }
 
         // Initialize a color background typed value.
@@ -3706,10 +3634,161 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         bareWebView.destroy();
     }
 
+    private void applyAppSettings() {
+        // Get a handle for the shared preferences.
+        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+        // Store the values from the shared preferences in variables.
+        incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
+        boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
+        sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
+        sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
+        sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
+        proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value));
+        fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
+        hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true);
+        scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
+
+        // Apply the saved proxy mode if the app has been restarted.
+        if (savedProxyMode != null) {
+            // Apply the saved proxy mode.
+            proxyMode = savedProxyMode;
+
+            // Reset the saved proxy mode.
+            savedProxyMode = null;
+        }
+
+        // Get the search string.
+        String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value));
+
+        // Set the search string.
+        if (searchString.equals("Custom URL")) {  // A custom search string is used.
+            searchURL = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value));
+        } else {  // A custom search string is not used.
+            searchURL = searchString;
+        }
+
+        // Apply the proxy.
+        applyProxy(false);
+
+        // Set Do Not Track status.
+        if (doNotTrackEnabled) {
+            customHeaders.put("DNT", "1");
+        } else {
+            customHeaders.remove("DNT");
+        }
+
+        // Get the current layout parameters.  Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command.
+        CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
+        AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
+        AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams();
+        AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams();
+
+        // Add the scrolling behavior to the layout parameters.
+        if (scrollAppBar) {
+            // Enable scrolling of the app bar.
+            swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
+            toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+            findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+            tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+        } else {
+            // Disable scrolling of the app bar.
+            swipeRefreshLayoutParams.setBehavior(null);
+            toolbarLayoutParams.setScrollFlags(0);
+            findOnPageLayoutParams.setScrollFlags(0);
+            tabsLayoutParams.setScrollFlags(0);
+
+            // Expand the app bar if it is currently collapsed.
+            appBarLayout.setExpanded(true);
+        }
+
+        // Apply the modified layout parameters.
+        swipeRefreshLayout.setLayoutParams(swipeRefreshLayoutParams);
+        toolbar.setLayoutParams(toolbarLayoutParams);
+        findOnPageLinearLayout.setLayoutParams(findOnPageLayoutParams);
+        tabsLinearLayout.setLayoutParams(tabsLayoutParams);
+
+        // Set the app bar scrolling for each WebView.
+        for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+            // Get the WebView tab fragment.
+            WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
+
+            // Get the fragment view.
+            View fragmentView = webViewTabFragment.getView();
+
+            // Only modify the WebViews if they exist.
+            if (fragmentView != null) {
+                // Get the nested scroll WebView from the tab fragment.
+                NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+                // Set the app bar scrolling.
+                nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar);
+            }
+        }
+
+        // Update the full screen browsing mode settings.
+        if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) {  // Privacy Browser is currently in full screen browsing mode.
+            // Update the visibility of the app bar, which might have changed in the settings.
+            if (hideAppBar) {
+                // Hide the tab linear layout.
+                tabsLinearLayout.setVisibility(View.GONE);
+
+                // Hide the action bar.
+                actionBar.hide();
+            } else {
+                // Show the tab linear layout.
+                tabsLinearLayout.setVisibility(View.VISIBLE);
+
+                // Show the action bar.
+                actionBar.show();
+            }
+
+            // Hide the banner ad in the free flavor.
+            if (BuildConfig.FLAVOR.contentEquals("free")) {
+                // Get a handle for the ad view.  This cannot be a class variable because it changes with each ad load.
+                View adView = findViewById(R.id.adview);
+
+                // Hide the banner ad.
+                AdHelper.hideAd(adView);
+            }
+
+            /* Hide the system bars.
+             * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
+             * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
+             * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
+             * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
+             */
+            rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+                    View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+        } else {  // Privacy Browser is not in full screen browsing mode.
+            // Reset the full screen tracker, which could be true if Privacy Browser was in full screen mode before entering settings and full screen browsing was disabled.
+            inFullScreenBrowsingMode = false;
+
+            // Show the tab linear layout.
+            tabsLinearLayout.setVisibility(View.VISIBLE);
+
+            // Show the action bar.
+            actionBar.show();
+
+            // Show the banner ad in the free flavor.
+            if (BuildConfig.FLAVOR.contentEquals("free")) {
+                // Get a handle for the ad view.  This cannot be a class variable because it changes with each ad load.
+                View adView = findViewById(R.id.adview);
+
+                // Initialize the ads.  If this isn't the first run, `loadAd()` will be automatically called instead.
+                // `getContext()` can be used instead of `getActivity.getApplicationContext()` once the minimum API >= 23.
+                AdHelper.initializeAds(adView, getApplicationContext(), this, getSupportFragmentManager(), getString(R.string.ad_unit_id));
+            }
+
+            // Remove the `SYSTEM_UI` flags from the root frame layout.
+            rootFrameLayout.setSystemUiVisibility(0);
+        }
+    }
+
     @Override
     public void navigateHistory(String url, int steps) {
         // Apply the domain settings.
-        applyDomainSettings(currentWebView, url, false, false);
+        applyDomainSettings(currentWebView, url, false, false, false);
 
         // Load the history entry.
         currentWebView.goBackOrForward(steps);
@@ -3724,7 +3803,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
 
         // Apply the domain settings.
-        applyDomainSettings(currentWebView, previousUrl, false, false);
+        applyDomainSettings(currentWebView, previousUrl, false, false, false);
 
         // Go back.
         currentWebView.goBack();
@@ -3732,10 +3811,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     // `reloadWebsite` is used if returning from the Domains activity.  Otherwise JavaScript might not function correctly if it is newly enabled.
     @SuppressLint("SetJavaScriptEnabled")
-    private boolean applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite) {
-        // Store a copy of the current user agent to track changes for the return boolean.
-        String initialUserAgent = nestedScrollWebView.getSettings().getUserAgentString();
-
+    private void applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite, boolean loadUrl) {
         // Store the current URL.
         nestedScrollWebView.setCurrentUrl(url);
 
@@ -3770,9 +3846,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get the current page position.
                 int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
 
-                // Get a handle for the tab layout.
-                TabLayout tabLayout = findViewById(R.id.tablayout);
-
                 // Get the corresponding tab.
                 TabLayout.Tab tab = tabLayout.getTabAt(currentPagePosition);
 
@@ -3857,16 +3930,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             String defaultFontSizeString = sharedPreferences.getString("font_size", getString(R.string.font_size_default_value));
             String defaultUserAgentName = sharedPreferences.getString("user_agent", getString(R.string.user_agent_default_value));
             boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
+            String webViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value));
             boolean wideViewport = sharedPreferences.getBoolean("wide_viewport", true);
             boolean displayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true);
 
+            // Get the WebView theme entry values string array.
+            String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values);
+
             // Get a handle for the cookie manager.
             CookieManager cookieManager = CookieManager.getInstance();
 
-            // Get handles for the views.
-            RelativeLayout urlRelativeLayout = findViewById(R.id.url_relativelayout);
-            SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
-
             // Initialize the user agent array adapter and string array.
             ArrayAdapter<CharSequence> userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item);
             String[] userAgentDataArray = getResources().getStringArray(R.array.user_agent_data);
@@ -3878,7 +3951,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Get the settings from the cursor.
                 nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID)));
-                nestedScrollWebView.setDomainSettingsJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
+                nestedScrollWebView.getSettings().setJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
                 nestedScrollWebView.setAcceptFirstPartyCookies(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1);
                 boolean domainThirdPartyCookiesEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1);
                 nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
@@ -3900,7 +3973,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 String userAgentName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
                 int fontSize = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
                 int swipeToRefreshInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
-                int nightModeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE));
+                int webViewThemeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.WEBVIEW_THEME));
                 int wideViewportInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.WIDE_VIEWPORT));
                 int displayWebpageImagesInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
                 boolean pinnedSslCertificate = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1);
@@ -3913,24 +3986,31 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 boolean pinnedIpAddresses = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)) == 1);
                 String pinnedHostIpAddresses = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES));
 
-                // Create the pinned SSL date variables.
+                // Get the pinned SSL date longs.
+                long pinnedSslStartDateLong = currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE));
+                long pinnedSslEndDateLong = currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE));
+
+                // Define the pinned SSL date variables.
                 Date pinnedSslStartDate;
                 Date pinnedSslEndDate;
 
-                // Set the pinned SSL certificate start date to `null` if the saved date `long` is 0 because creating a new Date results in an error if the input is 0.
-                if (currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) == 0) {
+                // Set the pinned SSL certificate start date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0.
+                if (pinnedSslStartDateLong == 0) {
                     pinnedSslStartDate = null;
                 } else {
-                    pinnedSslStartDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)));
+                    pinnedSslStartDate = new Date(pinnedSslStartDateLong);
                 }
 
-                // Set the pinned SSL certificate end date to `null` if the saved date `long` is 0 because creating a new Date results in an error if the input is 0.
-                if (currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) == 0) {
+                // Set the pinned SSL certificate end date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0.
+                if (pinnedSslEndDateLong == 0) {
                     pinnedSslEndDate = null;
                 } else {
-                    pinnedSslEndDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)));
+                    pinnedSslEndDate = new Date(pinnedSslEndDateLong);
                 }
 
+                // Close the current host domain settings cursor.
+                currentDomainSettingsCursor.close();
+
                 // If there is a pinned SSL certificate, store it in the WebView.
                 if (pinnedSslCertificate) {
                     nestedScrollWebView.setPinnedSslCertificate(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName,
@@ -3942,37 +4022,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     nestedScrollWebView.setPinnedIpAddresses(pinnedHostIpAddresses);
                 }
 
-                // Set night mode according to the night mode int.
-                switch (nightModeInt) {
-                    case DomainsDatabaseHelper.SYSTEM_DEFAULT:
-                        // Set night mode according to the current default.
-                        nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false));
-                        break;
-
-                    case DomainsDatabaseHelper.ENABLED:
-                        // Enable night mode.
-                        nestedScrollWebView.setNightMode(true);
-                        break;
-
-                    case DomainsDatabaseHelper.DISABLED:
-                        // Disable night mode.
-                        nestedScrollWebView.setNightMode(false);
-                        break;
-                }
-
-                // Enable JavaScript if night mode is enabled.
-                if (nestedScrollWebView.getNightMode()) {
-                    // Enable JavaScript.
-                    nestedScrollWebView.getSettings().setJavaScriptEnabled(true);
-                } else {
-                    // Set JavaScript according to the domain settings.
-                    nestedScrollWebView.getSettings().setJavaScriptEnabled(nestedScrollWebView.getDomainSettingsJavaScriptEnabled());
-                }
-
-                // Close the current host domain settings cursor.
-                currentDomainSettingsCursor.close();
-
-                // Apply the domain settings.
+                // Apply the cookie domain settings.
                 cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies());
 
                 // Set third-party cookies status if API >= 21.
@@ -4050,26 +4100,71 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         // Store the swipe to refresh status in the nested scroll WebView.
                         nestedScrollWebView.setSwipeToRefresh(defaultSwipeToRefresh);
 
-                        // Apply swipe to refresh according to the default.  This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener.
-                        swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
+                        // Update the swipe refresh layout.
+                        if (defaultSwipeToRefresh) {  // Swipe to refresh is enabled.
+                            // Only enable the swipe refresh layout if the WebView is scrolled to the top.  It is updated every time the scroll changes.
+                            swipeRefreshLayout.setEnabled(currentWebView.getScrollY() == 0);
+                        } else {  // Swipe to refresh is disabled.
+                            // Disable the swipe refresh layout.
+                            swipeRefreshLayout.setEnabled(false);
+                        }
                         break;
 
                     case DomainsDatabaseHelper.ENABLED:
                         // Store the swipe to refresh status in the nested scroll WebView.
                         nestedScrollWebView.setSwipeToRefresh(true);
 
-                        // Enable swipe to refresh.  This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener.
-                        swipeRefreshLayout.setEnabled(true);
+                        // Only enable the swipe refresh layout if the WebView is scrolled to the top.  It is updated every time the scroll changes.
+                        swipeRefreshLayout.setEnabled(currentWebView.getScrollY() == 0);
                         break;
 
                     case DomainsDatabaseHelper.DISABLED:
                         // Store the swipe to refresh status in the nested scroll WebView.
                         nestedScrollWebView.setSwipeToRefresh(false);
 
-                        // Disable swipe to refresh.  This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener.
+                        // Disable swipe to refresh.
                         swipeRefreshLayout.setEnabled(false);
                 }
 
+                // Check to see if WebView themes are supported.
+                if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+                    // Set the WebView theme.
+                    switch (webViewThemeInt) {
+                        case DomainsDatabaseHelper.SYSTEM_DEFAULT:
+                            // Set the WebView theme.  A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
+                            if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) {  // The light theme is selected.
+                                // Turn off the WebView dark mode.
+                                WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+                            } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
+                                // Turn on the WebView dark mode.
+                                WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+                            } else {  // The system default theme is selected.
+                                // Get the current system theme status.
+                                int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+                                // Set the WebView theme according to the current system theme status.
+                                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {  // The system is in day mode.
+                                    // Turn off the WebView dark mode.
+                                    WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+                                } else {  // The system is in night mode.
+                                    // Turn on the WebView dark mode.
+                                    WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+                                }
+                            }
+                            break;
+
+                        case DomainsDatabaseHelper.LIGHT_THEME:
+                            // Turn off the WebView dark mode.
+                            WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+                            break;
+
+                        case DomainsDatabaseHelper.DARK_THEME:
+                            // Turn on the WebView dark mode.
+                            WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+                            break;
+                    }
+                }
+
                 // Set the viewport.
                 switch (wideViewportInt) {
                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
@@ -4103,15 +4198,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get the current theme status.
                 int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
 
-                // Set a background on the URL relative layout to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
+                // Set a background on the URL relative layout to indicate that custom domain settings are being used.
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+                    urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_light_green, null));
                 } else {
-                    urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
+                    urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_dark_blue, null));
                 }
             } else {  // The new URL does not have custom domain settings.  Load the defaults.
                 // Store the values from the shared preferences.
-                boolean defaultJavaScriptEnabled = sharedPreferences.getBoolean("javascript", false);
+                nestedScrollWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false));
                 nestedScrollWebView.setAcceptFirstPartyCookies(sharedPreferences.getBoolean("first_party_cookies", false));
                 boolean defaultThirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false);
                 nestedScrollWebView.getSettings().setDomStorageEnabled(sharedPreferences.getBoolean("dom_storage", false));
@@ -4123,16 +4218,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, sharedPreferences.getBoolean("ultralist", true));
                 nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, sharedPreferences.getBoolean("ultraprivacy", true));
                 nestedScrollWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, sharedPreferences.getBoolean("block_all_third_party_requests", false));
-                nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false));
-
-                // Enable JavaScript if night mode is enabled.
-                if (nestedScrollWebView.getNightMode()) {
-                    // Enable JavaScript.
-                    nestedScrollWebView.getSettings().setJavaScriptEnabled(true);
-                } else {
-                    // Set JavaScript according to the domain settings.
-                    nestedScrollWebView.getSettings().setJavaScriptEnabled(defaultJavaScriptEnabled);
-                }
 
                 // Apply the default first-party cookie setting.
                 cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies());
@@ -4154,8 +4239,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Store the swipe to refresh status in the nested scroll WebView.
                 nestedScrollWebView.setSwipeToRefresh(defaultSwipeToRefresh);
 
-                // Apply swipe to refresh according to the default.
-                swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
+                // Update the swipe refresh layout.
+                if (defaultSwipeToRefresh) {  // Swipe to refresh is enabled.
+                    // Only enable the swipe refresh layout if the WebView is scrolled to the top.  It is updated every time the scroll changes.
+                    swipeRefreshLayout.setEnabled(currentWebView.getScrollY() == 0);
+                } else {  // Swipe to refresh is disabled.
+                    // Disable the swipe refresh layout.
+                    swipeRefreshLayout.setEnabled(false);
+                }
 
                 // Reset the pinned variables.
                 nestedScrollWebView.setDomainSettingsDatabaseId(-1);
@@ -4190,14 +4281,38 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
                 }
 
+                // Apply the WebView theme if supported by the installed WebView.
+                if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+                    // Set the WebView theme.  A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
+                    if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) {  // The light theme is selected.
+                        // Turn off the WebView dark mode.
+                        WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+                    } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
+                        // Turn on the WebView dark mode.
+                        WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+                    } else {  // The system default theme is selected.
+                        // Get the current system theme status.
+                        int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+                        // Set the WebView theme according to the current system theme status.
+                        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {  // The system is in day mode.
+                            // Turn off the WebView dark mode.
+                            WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+                        } else {  // The system is in night mode.
+                            // Turn on the WebView dark mode.
+                            WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+                        }
+                    }
+                }
+
                 // Set the viewport.
                 nestedScrollWebView.getSettings().setUseWideViewPort(wideViewport);
 
                 // Set the loading of webpage images.
                 nestedScrollWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
 
-                // Set a transparent background on URL edit text.  The deprecated `getResources().getDrawable()` must be used until the minimum API >= 21.
-                urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent));
+                // Set a transparent background on the URL relative layout.
+                urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.color.transparent, null));
             }
 
             // Close the domains database helper.
@@ -4212,14 +4327,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             nestedScrollWebView.reload();
         }
 
-        // Return the user agent changed status.
-        return !nestedScrollWebView.getSettings().getUserAgentString().equals(initialUserAgent);
+        // Load the URL if directed.  This makes sure that the domain settings are properly loaded before the URL.  By using `loadUrl()`, instead of `loadUrlFromBase()`, the Referer header will never be sent.
+        if (loadUrl) {
+            nestedScrollWebView.loadUrl(url, customHeaders);
+        }
     }
 
     private void applyProxy(boolean reloadWebViews) {
-        // Get a handle for the app bar layout.
-        AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
-
         // Set the proxy according to the mode.  `this` refers to the current activity where an alert dialog might be displayed.
         ProxyHelper.setProxy(getApplicationContext(), appBarLayout, proxyMode);
 
@@ -4247,10 +4361,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case ProxyHelper.TOR:
                 // Set the app bar background to indicate proxying through Orbot is enabled.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    appBarLayout.setBackgroundResource(R.color.dark_blue_30);
-                } else {
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
                     appBarLayout.setBackgroundResource(R.color.blue_50);
+                } else {
+                    appBarLayout.setBackgroundResource(R.color.dark_blue_30);
                 }
 
                 // Check to see if Orbot is installed.
@@ -4289,10 +4403,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case ProxyHelper.I2P:
                 // Set the app bar background to indicate proxying through Orbot is enabled.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    appBarLayout.setBackgroundResource(R.color.dark_blue_30);
-                } else {
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
                     appBarLayout.setBackgroundResource(R.color.blue_50);
+                } else {
+                    appBarLayout.setBackgroundResource(R.color.dark_blue_30);
                 }
 
                 // Check to see if I2P is installed.
@@ -4316,10 +4430,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case ProxyHelper.CUSTOM:
                 // Set the app bar background to indicate proxying through Orbot is enabled.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    appBarLayout.setBackgroundResource(R.color.dark_blue_30);
-                } else {
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
                     appBarLayout.setBackgroundResource(R.color.blue_50);
+                } else {
+                    appBarLayout.setBackgroundResource(R.color.dark_blue_30);
                 }
                 break;
         }
@@ -4349,19 +4463,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     private void updatePrivacyIcons(boolean runInvalidateOptionsMenu) {
         // Only update the privacy icons if the options menu and the current WebView have already been populated.
         if ((optionsMenu != null) && (currentWebView != null)) {
-            // Get handles for the menu items.
-            MenuItem privacyMenuItem = optionsMenu.findItem(R.id.toggle_javascript);
-            MenuItem firstPartyCookiesMenuItem = optionsMenu.findItem(R.id.toggle_first_party_cookies);
-            MenuItem domStorageMenuItem = optionsMenu.findItem(R.id.toggle_dom_storage);
-            MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh);
-
             // Update the privacy icon.
             if (currentWebView.getSettings().getJavaScriptEnabled()) {  // JavaScript is enabled.
-                privacyMenuItem.setIcon(R.drawable.javascript_enabled);
+                optionsPrivacyMenuItem.setIcon(R.drawable.javascript_enabled);
             } else if (currentWebView.getAcceptFirstPartyCookies()) {  // JavaScript is disabled but cookies are enabled.
-                privacyMenuItem.setIcon(R.drawable.warning);
+                optionsPrivacyMenuItem.setIcon(R.drawable.warning);
             } else {  // All the dangerous features are disabled.
-                privacyMenuItem.setIcon(R.drawable.privacy_mode);
+                optionsPrivacyMenuItem.setIcon(R.drawable.privacy_mode);
             }
 
             // Get the current theme status.
@@ -4369,39 +4477,32 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             // Update the first-party cookies icon.
             if (currentWebView.getAcceptFirstPartyCookies()) {  // First-party cookies are enabled.
-                firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled);
+                optionsFirstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled);
             } else {  // First-party cookies are disabled.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night);
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+                    optionsFirstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_day);
                 } else {
-                    firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_day);
+                    optionsFirstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night);
                 }
             }
 
-            // Update the DOM storage icon.
-            if (currentWebView.getSettings().getJavaScriptEnabled() && currentWebView.getSettings().getDomStorageEnabled()) {  // Both JavaScript and DOM storage are enabled.
-                domStorageMenuItem.setIcon(R.drawable.dom_storage_enabled);
-            } else if (currentWebView.getSettings().getJavaScriptEnabled()) {  // JavaScript is enabled but DOM storage is disabled.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_night);
+            // Update the refresh icon.
+            if (optionsRefreshMenuItem.getTitle() == getString(R.string.refresh)) {  // The refresh icon is displayed.
+                // Set the icon according to the theme.
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+                    optionsRefreshMenuItem.setIcon(R.drawable.refresh_enabled_day);
                 } else {
-                    domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_day);
+                    optionsRefreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
                 }
-            } else {  // JavaScript is disabled, so DOM storage is ghosted.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_night);
+            } else {  // The stop icon is displayed.
+                // Set the icon according to the theme.
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+                    optionsRefreshMenuItem.setIcon(R.drawable.close_blue_day);
                 } else {
-                    domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_day);
+                    optionsRefreshMenuItem.setIcon(R.drawable.close_blue_night);
                 }
             }
 
-            // Update the refresh icon.
-            if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
-            } else {
-                refreshMenuItem.setIcon(R.drawable.refresh_enabled_day);
-            }
-
             // `invalidateOptionsMenu()` calls `onPrepareOptionsMenu()` and redraws the icons in the app bar.
             if (runInvalidateOptionsMenu) {
                 invalidateOptionsMenu();
@@ -4410,9 +4511,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     private void highlightUrlText() {
-        // Get a handle for the URL edit text.
-        EditText urlEditText = findViewById(R.id.url_edittext);
-
         // Only highlight the URL text if the box is not currently selected.
         if (!urlEditText.hasFocus()) {
             // Get the URL string.
@@ -4625,8 +4723,99 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         ultraList = combinedBlocklists.get(4);
         ultraPrivacy = combinedBlocklists.get(5);
 
-        // Add the first tab.
-        addNewTab("", true);
+        // Check to see if the activity has been restarted with a saved state.
+        if ((savedStateArrayList == null) || (savedStateArrayList.size() == 0)) {  // The activity has not been restarted or it was restarted on start to force the night theme.
+            // Add the first tab.
+            addNewTab("", true);
+        } else {  // The activity has been restarted.
+            // Restore each tab.  Once the minimum API >= 24, a `forEach()` command can be used.
+            for (int i = 0; i < savedStateArrayList.size(); i++) {
+                // Add a new tab.
+                tabLayout.addTab(tabLayout.newTab());
+
+                // Get the new tab.
+                TabLayout.Tab newTab = tabLayout.getTabAt(i);
+
+                // Remove the lint warning below that the current tab might be null.
+                assert newTab != null;
+
+                // Set a custom view on the new tab.
+                newTab.setCustomView(R.layout.tab_custom_view);
+
+                // Add the new page.
+                webViewPagerAdapter.restorePage(savedStateArrayList.get(i), savedNestedScrollWebViewStateArrayList.get(i));
+            }
+
+            // Reset the saved state variables.
+            savedStateArrayList = null;
+            savedNestedScrollWebViewStateArrayList = null;
+
+            // Restore the selected tab position.
+            if (savedTabPosition == 0) {  // The first tab is selected.
+                // Set the first page as the current WebView.
+                setCurrentWebView(0);
+            } else {  // the first tab is not selected.
+                // Move to the selected tab.
+                webViewPager.setCurrentItem(savedTabPosition);
+            }
+
+            // Get the intent that started the app.
+            Intent intent = getIntent();
+
+            // Reset the intent.  This prevents a duplicate tab from being created on restart.
+            setIntent(new Intent());
+
+            // Get the information from the intent.
+            String intentAction = intent.getAction();
+            Uri intentUriData = intent.getData();
+            String intentStringExtra = intent.getStringExtra(Intent.EXTRA_TEXT);
+
+            // Determine if this is a web search.
+            boolean isWebSearch = ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH));
+
+            // Only process the URI if it contains data or it is a web search.  If the user pressed the desktop icon after the app was already running the URI will be null.
+            if (intentUriData != null || intentStringExtra != null || isWebSearch) {
+                // Get the shared preferences.
+                SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+                // Create a URL string.
+                String url;
+
+                // If the intent action is a web search, perform the search.
+                if (isWebSearch) {  // The intent is a web search.
+                    // Create an encoded URL string.
+                    String encodedUrlString;
+
+                    // Sanitize the search input and convert it to a search.
+                    try {
+                        encodedUrlString = URLEncoder.encode(intent.getStringExtra(SearchManager.QUERY), "UTF-8");
+                    } catch (UnsupportedEncodingException exception) {
+                        encodedUrlString = "";
+                    }
+
+                    // Add the base search URL.
+                    url = searchURL + encodedUrlString;
+                } else if (intentUriData != null) {  // The intent contains a URL formatted as a URI.
+                    // Set the intent data as the URL.
+                    url = intentUriData.toString();
+                } else {  // The intent contains a string, which might be a URL.
+                    // Set the intent string as the URL.
+                    url = intentStringExtra;
+                }
+
+                // Add a new tab if specified in the preferences.
+                if (sharedPreferences.getBoolean("open_intents_in_new_tab", true)) {  // Load the URL in a new tab.
+                    // Set the loading new intent flag.
+                    loadingNewIntent = true;
+
+                    // Add a new tab.
+                    addNewTab(url, true);
+                } else {  // Load the URL in the current tab.
+                    // Make it so.
+                    loadUrl(currentWebView, url);
+                }
+            }
+        }
     }
 
     public void addTab(View view) {
@@ -4635,13 +4824,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     private void addNewTab(String url, boolean moveToTab) {
-        // Sanitize the URL.
-        url = sanitizeUrl(url);
-
-        // Get a handle for the tab layout and the view pager.
-        TabLayout tabLayout = findViewById(R.id.tablayout);
-        ViewPager webViewPager = findViewById(R.id.webviewpager);
-
         // Get the new page number.  The page numbers are 0 indexed, so the new page number will match the current count.
         int newTabNumber = tabLayout.getTabCount();
 
@@ -4662,9 +4844,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     public void closeTab(View view) {
-        // Get a handle for the tab layout.
-        TabLayout tabLayout = findViewById(R.id.tablayout);
-
         // Run the command according to the number of tabs.
         if (tabLayout.getTabCount() > 1) {  // There is more than one tab open.
             // Close the current tab.
@@ -4675,10 +4854,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     private void closeCurrentTab() {
-        // Get handles for the views.
-        AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
-        TabLayout tabLayout = findViewById(R.id.tablayout);
-        ViewPager webViewPager = findViewById(R.id.webviewpager);
+        // Pause the current WebView.  This prevents buffered audio from playing after the tab is closed.
+        currentWebView.onPause();
 
         // Get the current tab number.
         int currentTabNumber = tabLayout.getSelectedTabPosition();
@@ -4695,6 +4872,48 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         appBarLayout.setExpanded(true);
     }
 
+    private void saveWebpageArchive(String filePath) {
+        // Save the webpage archive.
+        currentWebView.saveWebArchive(filePath);
+
+        // Display a snackbar.
+        Snackbar saveWebpageArchiveSnackbar = Snackbar.make(currentWebView, getString(R.string.file_saved) + "  " + filePath, Snackbar.LENGTH_SHORT);
+
+        // Add an open option to the snackbar.
+        saveWebpageArchiveSnackbar.setAction(R.string.open, (View view) -> {
+            // Get a file for the file name string.
+            File file = new File(filePath);
+
+            // Declare a file URI variable.
+            Uri fileUri;
+
+            // Get the URI for the file according to the Android version.
+            if (Build.VERSION.SDK_INT >= 24) {  // Use a file provider.
+                fileUri = FileProvider.getUriForFile(this, getString(R.string.file_provider), file);
+            } else {  // Get the raw file path URI.
+                fileUri = Uri.fromFile(file);
+            }
+
+            // Get a handle for the content resolver.
+            ContentResolver contentResolver = getContentResolver();
+
+            // Create an open intent with `ACTION_VIEW`.
+            Intent openIntent = new Intent(Intent.ACTION_VIEW);
+
+            // Set the URI and the MIME type.
+            openIntent.setDataAndType(fileUri, contentResolver.getType(fileUri));
+
+            // Allow the app to read the file URI.
+            openIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+            // Show the chooser.
+            startActivity(Intent.createChooser(openIntent, getString(R.string.open)));
+        });
+
+        // Show the snackbar.
+        saveWebpageArchiveSnackbar.show();
+    }
+
     private void clearAndExit() {
         // Get a handle for the shared preferences.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
@@ -4783,6 +5002,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
         }
 
+        // Clear the logcat.
+        if (clearEverything || sharedPreferences.getBoolean(getString(R.string.clear_logcat_key), true)) {
+            try {
+                // Clear the logcat.  `-c` clears the logcat.  `-b all` clears all the buffers (instead of just crash, main, and system).
+                Process process = Runtime.getRuntime().exec("logcat -b all -c");
+
+                // Wait for the process to finish.
+                process.waitFor();
+            } catch (IOException|InterruptedException exception) {
+                // Do nothing.
+            }
+        }
+
         // Clear the cache.
         if (clearEverything || sharedPreferences.getBoolean("clear_cache", true)) {
             // Clear the cache from each WebView.
@@ -4839,7 +5071,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Clear the back/forward history for this WebView.
                 nestedScrollWebView.clearHistory();
 
-                // Destroy the internal state of `mainWebView`.
+                // Destroy the internal state of the WebView.
                 nestedScrollWebView.destroy();
             }
         }
@@ -4872,12 +5104,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         System.exit(0);
     }
 
-    private void setCurrentWebView(int pageNumber) {
-        // Get handles for the URL views.
-        RelativeLayout urlRelativeLayout = findViewById(R.id.url_relativelayout);
-        EditText urlEditText = findViewById(R.id.url_edittext);
-        SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
+    public void bookmarksBack(View view) {
+        if (currentBookmarksFolder.isEmpty()) {  // The home folder is displayed.
+            // close the bookmarks drawer.
+            drawerLayout.closeDrawer(GravityCompat.END);
+        } else {  // A subfolder is displayed.
+            // Place the former parent folder in `currentFolder`.
+            currentBookmarksFolder = bookmarksDatabaseHelper.getParentFolderName(currentBookmarksFolder);
+
+            // Load the new folder.
+            loadBookmarksFolder();
+        }
+    }
 
+    private void setCurrentWebView(int pageNumber) {
         // Stop the swipe to refresh indicator if it is running
         swipeRefreshLayout.setRefreshing(false);
 
@@ -4885,17 +5125,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(pageNumber);
 
         // Get the fragment view.
-        View fragmentView = webViewTabFragment.getView();
+        View webViewFragmentView = webViewTabFragment.getView();
 
         // Set the current WebView if the fragment view is not null.
-        if (fragmentView != null) {  // The fragment has been populated.
+        if (webViewFragmentView != null) {  // The fragment has been populated.
             // Store the current WebView.
-            currentWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+            currentWebView = webViewFragmentView.findViewById(R.id.nestedscroll_webview);
 
             // Update the status of swipe to refresh.
             if (currentWebView.getSwipeToRefresh()) {  // Swipe to refresh is enabled.
                 // Enable the swipe refresh layout if the WebView is scrolled all the way to the top.  It is updated every time the scroll changes.
-                swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
+                swipeRefreshLayout.setEnabled(currentWebView.getScrollY() == 0);
             } else {  // Swipe to refresh is disabled.
                 // Disable the swipe refresh layout.
                 swipeRefreshLayout.setEnabled(false);
@@ -4953,14 +5193,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get the current theme status.
                 int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
 
-                // Set a green background on the URL relative layout to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
+                // Set a green background on the URL relative layout to indicate that custom domain settings are being used.
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+                    urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_light_green, null));
                 } else {
-                    urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
+                    urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_dark_blue, null));
                 }
             } else {
-                urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent));
+                urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.color.transparent, null));
             }
         } else {  // The fragment has not been populated.  Try again in 100 milliseconds.
             // Create a handler to set the current WebView.
@@ -4978,22 +5218,47 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     @Override
-    public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url) {
-        // Get a handle for the app compat delegate.
-        AppCompatDelegate appCompatDelegate = getDelegate();
+    public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url, Boolean restoringState) {
+        // Get a handle for the shared preferences.
+        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
-        // Get handles for the activity views.
-        FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
-        DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
-        RelativeLayout mainContentRelativeLayout = findViewById(R.id.main_content_relativelayout);
-        ActionBar actionBar = appCompatDelegate.getSupportActionBar();
-        LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
-        EditText urlEditText = findViewById(R.id.url_edittext);
-        TabLayout tabLayout = findViewById(R.id.tablayout);
-        SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
+        // Get the WebView theme.
+        String webViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value));
+
+        // Get the WebView theme entry values string array.
+        String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values);
+
+        // Apply the WebView theme if supported by the installed WebView.
+        if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+            // Set the WebView theme.  A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
+            if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) {  // The light theme is selected.
+                // Turn off the WebView dark mode.
+                WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+
+                // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+                // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`.
+                nestedScrollWebView.setVisibility(View.VISIBLE);
+            } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
+                // Turn on the WebView dark mode.
+                WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+            } else {  // The system default theme is selected.
+                // Get the current system theme status.
+                int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
 
-        // Remove the incorrect lint warning below that the action bar might be null.
-        assert actionBar != null;
+                // Set the WebView theme according to the current system theme status.
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {  // The system is in day mode.
+                    // Turn off the WebView dark mode.
+                    WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+
+                    // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+                    // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`.
+                    nestedScrollWebView.setVisibility(View.VISIBLE);
+                } else {  // The system is in night mode.
+                    // Turn on the WebView dark mode.
+                    WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+                }
+            }
+        }
 
         // Get a handle for the activity
         Activity activity = this;
@@ -5007,9 +5272,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Remove the lint warning below that the input method manager might be null.
         assert inputMethodManager != null;
 
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
         // Initialize the favorite icon.
         nestedScrollWebView.initializeFavoriteIcon();
 
@@ -5055,8 +5317,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                             // Hide the action bar.
                             actionBar.hide();
 
-                            // If the app bar is not being scrolled, the swipe refresh layout needs to be adjusted.
-                            if (!scrollAppBar) {
+                            // Check to see if the app bar is normally scrolled.
+                            if (scrollAppBar) {  // The app bar is scrolled when it is displayed.
+                                // Get the swipe refresh layout parameters.
+                                CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
+
+                                // Remove the off-screen scrolling layout.
+                                swipeRefreshLayoutParams.setBehavior(null);
+                            } else {  // The app bar is not scrolled when it is displayed.
                                 // Remove the padding from the top of the swipe refresh layout.
                                 swipeRefreshLayout.setPadding(0, 0, 0, 0);
 
@@ -5067,7 +5335,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                         // Hide the banner ad in the free flavor.
                         if (BuildConfig.FLAVOR.contentEquals("free")) {
-                            AdHelper.hideAd(findViewById(R.id.adview));
+                            // Get a handle for the ad view.  This cannot be a class variable because it changes with each ad load.
+                            View adView = findViewById(R.id.adview);
+
+                            // Hide the banner ad.
+                            AdHelper.hideAd(adView);
                         }
 
                         /* Hide the system bars.
@@ -5087,8 +5359,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                             // Show the action bar.
                             actionBar.show();
 
-                            // If the app bar is not being scrolled, the swipe refresh layout needs to be adjusted.
-                            if (!scrollAppBar) {
+                            // Check to see if the app bar is normally scrolled.
+                            if (scrollAppBar) {  // The app bar is scrolled when it is displayed.
+                                // Get the swipe refresh layout parameters.
+                                CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
+
+                                // Add the off-screen scrolling layout.
+                                swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
+                            } else {  // The app bar is not scrolled when it is displayed.
                                 // The swipe refresh layout must be manually moved below the app bar layout.
                                 swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0);
 
@@ -5099,8 +5377,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                         // Show the banner ad in the free flavor.
                         if (BuildConfig.FLAVOR.contentEquals("free")) {
-                            // Reload the ad.
-                            AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_unit_id));
+                            // Get a handle for the ad view.  This cannot be a class variable because it changes with each ad load.
+                            View adView = findViewById(R.id.adview);
+
+                            // Reload the ad.  `getContext()` can be used instead of `getActivity.getApplicationContext()` once the minimum API >= 23.
+                            AdHelper.loadAd(adView, getApplicationContext(), activity, getString(R.string.ad_unit_id));
                         }
 
                         // Remove the `SYSTEM_UI` flags from the root frame layout.
@@ -5142,10 +5423,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
 
             // Get the file name from the content disposition.
-            String fileNameString = PrepareSaveDialog.getFileNameFromContentDisposition(this, contentDisposition, downloadUrl);
+            String fileNameString = PrepareSaveDialog.getFileNameFromHeaders(this, contentDisposition, mimetype, downloadUrl);
 
             // Instantiate the save dialog.
-            DialogFragment saveDialogFragment = SaveDialog.saveUrl(StoragePermissionDialog.SAVE_URL, downloadUrl, formattedFileSizeString, fileNameString, userAgent,
+            DialogFragment saveDialogFragment = SaveWebpageDialog.saveWebpage(StoragePermissionDialog.SAVE_URL, downloadUrl, formattedFileSizeString, fileNameString, userAgent,
                     nestedScrollWebView.getAcceptFirstPartyCookies());
 
             // Show the save dialog.  It must be named `save_dialog` so that the file picker can update the file name.
@@ -5176,62 +5457,61 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         });
 
         // Update the status of swipe to refresh based on the scroll position of the nested scroll WebView.  Also reinforce full screen browsing mode.
-        // Once the minimum API >= 23 this can be replaced with `nestedScrollWebView.setOnScrollChangeListener()`.
-        nestedScrollWebView.getViewTreeObserver().addOnScrollChangedListener(() -> {
-            if (nestedScrollWebView.getSwipeToRefresh()) {
-                // Only enable swipe to refresh if the WebView is scrolled to the top.
-                swipeRefreshLayout.setEnabled(nestedScrollWebView.getScrollY() == 0);
-            }
+        // On API < 23, `getViewTreeObserver().addOnScrollChangedListener()` must be used, but it is a little bit buggy and appears to get garbage collected from time to time.
+        if (Build.VERSION.SDK_INT >= 23) {
+            nestedScrollWebView.setOnScrollChangeListener((view, i, i1, i2, i3) -> {
+                if (nestedScrollWebView.getSwipeToRefresh()) {
+                    // Only enable swipe to refresh if the WebView is scrolled to the top.
+                    swipeRefreshLayout.setEnabled(nestedScrollWebView.getScrollY() == 0);
+                } else {
+                    // Disable swipe to refresh.
+                    swipeRefreshLayout.setEnabled(false);
+                }
 
-            // Reinforce the system UI visibility flags if in full screen browsing mode.
-            // This hides the status and navigation bars, which are displayed if other elements are shown, like dialog boxes, the options menu, or the keyboard.
-            if (inFullScreenBrowsingMode) {
-                /* Hide the system bars.
-                 * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
-                 * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
-                 * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
-                 * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
-                 */
-                rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
-                        View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
-            }
-        });
+                // Reinforce the system UI visibility flags if in full screen browsing mode.
+                // This hides the status and navigation bars, which are displayed if other elements are shown, like dialog boxes, the options menu, or the keyboard.
+                if (inFullScreenBrowsingMode) {
+                    /* Hide the system bars.
+                     * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
+                     * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
+                     * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
+                     * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
+                     */
+                    rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+                            View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+                }
+            });
+        } else {
+            nestedScrollWebView.getViewTreeObserver().addOnScrollChangedListener(() -> {
+                if (nestedScrollWebView.getSwipeToRefresh()) {
+                    // Only enable swipe to refresh if the WebView is scrolled to the top.
+                    swipeRefreshLayout.setEnabled(nestedScrollWebView.getScrollY() == 0);
+                } else {
+                    // Disable swipe to refresh.
+                    swipeRefreshLayout.setEnabled(false);
+                }
+
+
+                // Reinforce the system UI visibility flags if in full screen browsing mode.
+                // This hides the status and navigation bars, which are displayed if other elements are shown, like dialog boxes, the options menu, or the keyboard.
+                if (inFullScreenBrowsingMode) {
+                    /* Hide the system bars.
+                     * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
+                     * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
+                     * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
+                     * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
+                     */
+                    rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+                            View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+                }
+            });
+        }
 
         // Set the web chrome client.
         nestedScrollWebView.setWebChromeClient(new WebChromeClient() {
             // Update the progress bar when a page is loading.
             @Override
             public void onProgressChanged(WebView view, int progress) {
-                // Inject the night mode CSS if night mode is enabled.
-                if (nestedScrollWebView.getNightMode()) {  // Night mode is enabled.
-                    // `background-color: #212121` sets the background to be dark gray.  `color: #BDBDBD` sets the text color to be light gray.  `box-shadow: none` removes a lower underline on links
-                    // used by WordPress.  `text-decoration: none` removes all text underlines.  `text-shadow: none` removes text shadows, which usually have a hard coded color.
-                    // `border: none` removes all borders, which can also be used to underline text.  `a {color: #1565C0}` sets links to be a dark blue.
-                    // `::selection {background: #0D47A1}' sets the text selection highlight color to be a dark blue. `!important` takes precedent over any existing sub-settings.
-                    nestedScrollWebView.evaluateJavascript("(function() {var parent = document.getElementsByTagName('head').item(0); var style = document.createElement('style'); style.type = 'text/css'; " +
-                            "style.innerHTML = '* {background-color: #212121 !important; color: #BDBDBD !important; box-shadow: none !important; text-decoration: none !important;" +
-                            "text-shadow: none !important; border: none !important;} a {color: #1565C0 !important;} ::selection {background: #0D47A1 !important;}'; parent.appendChild(style)})()", value -> {
-                        // Initialize a handler to display `mainWebView`.
-                        Handler displayWebViewHandler = new Handler();
-
-                        // Setup a runnable to display `mainWebView` after a delay to allow the CSS to be applied.
-                        Runnable displayWebViewRunnable = () -> {
-                            // Only display `mainWebView` if the progress bar is gone.  This prevents the display of the `WebView` while it is still loading.
-                            if (progressBar.getVisibility() == View.GONE) {
-                                nestedScrollWebView.setVisibility(View.VISIBLE);
-                            }
-                        };
-
-                        // Display the WebView after 500 milliseconds.
-                        displayWebViewHandler.postDelayed(displayWebViewRunnable, 500);
-                    });
-                } else {  // Night mode is disabled.
-                    // Display the nested scroll WebView if night mode is disabled.
-                    // Because of a race condition between `applyDomainSettings` and `onPageStarted`,
-                    // when night mode is set by domain settings the WebView may be hidden even if night mode is not currently enabled.
-                    nestedScrollWebView.setVisibility(View.VISIBLE);
-                }
-
                 // Update the progress bar.
                 progressBar.setProgress(progress);
 
@@ -5245,6 +5525,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                     //Stop the swipe to refresh indicator if it is running
                     swipeRefreshLayout.setRefreshing(false);
+
+                    // Make the current WebView visible.  If this is a new tab, the current WebView would have been created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+                    nestedScrollWebView.setVisibility(View.VISIBLE);
                 }
             }
 
@@ -5293,19 +5576,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // Get the custom view from the tab.
                     View tabView = tab.getCustomView();
 
-                    // Remove the incorrect warning below that the current tab view might be null.
-                    assert tabView != null;
+                    // Only populate the title text view if the tab view has been fully populated.
+                    if (tabView != null) {
+                        // Get the title text view from the tab.
+                        TextView tabTitleTextView = tabView.findViewById(R.id.title_textview);
 
-                    // Get the title text view from the tab.
-                    TextView tabTitleTextView = tabView.findViewById(R.id.title_textview);
-
-                    // Set the title according to the URL.
-                    if (title.equals("about:blank")) {
-                        // Set the title to indicate a new tab.
-                        tabTitleTextView.setText(R.string.new_tab);
-                    } else {
-                        // Set the title as the tab text.
-                        tabTitleTextView.setText(title);
+                        // Set the title according to the URL.
+                        if (title.equals("about:blank")) {
+                            // Set the title to indicate a new tab.
+                            tabTitleTextView.setText(R.string.new_tab);
+                        } else {
+                            // Set the title as the tab text.
+                            tabTitleTextView.setText(title);
+                        }
                     }
                 }
             }
@@ -5313,16 +5596,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Enter full screen video.
             @Override
             public void onShowCustomView(View video, CustomViewCallback callback) {
-                // Get a handle for the full screen video frame layout.
-                FrameLayout fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout);
-
                 // Set the full screen video flag.
                 displayingFullScreenVideo = true;
 
                 // Pause the ad if this is the free flavor.
                 if (BuildConfig.FLAVOR.contentEquals("free")) {
+                    // Get a handle for the ad view.  This cannot be a class variable because it changes with each ad load.
+                    View adView = findViewById(R.id.adview);
+
                     // The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
-                    AdHelper.pauseAd(findViewById(R.id.adview));
+                    AdHelper.pauseAd(adView);
                 }
 
                 // Hide the keyboard.
@@ -5356,9 +5639,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Exit full screen video.
             @Override
             public void onHideCustomView() {
-                // Get a handle for the full screen video frame layout.
-                FrameLayout fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout);
-
                 // Re-enable the screen timeout.
                 fullScreenVideoFrameLayout.setKeepScreenOn(false);
 
@@ -5390,7 +5670,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                     // Hide the banner ad in the free flavor.
                     if (BuildConfig.FLAVOR.contentEquals("free")) {
-                        AdHelper.hideAd(findViewById(R.id.adview));
+                        // Get a handle for the ad view.  This cannot be a class variable because it changes with each ad load.
+                        View adView = findViewById(R.id.adview);
+
+                        // Hide the banner ad.
+                        AdHelper.hideAd(adView);
                     }
 
                     /* Hide the system bars.
@@ -5408,8 +5692,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Reload the ad for the free flavor if not in full screen mode.
                 if (BuildConfig.FLAVOR.contentEquals("free") && !inFullScreenBrowsingMode) {
-                    // Reload the ad.
-                    AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_unit_id));
+                    // Get a handle for the ad view.  This cannot be a class variable because it changes with each ad load.
+                    View adView = findViewById(R.id.adview);
+
+                    // Reload the ad.  `getContext()` can be used instead of `getActivity.getApplicationContext()` once the minimum API >= 23.
+                    AdHelper.loadAd(adView, getApplicationContext(), activity, getString(R.string.ad_unit_id));
                 }
             }
 
@@ -5450,28 +5737,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         });
 
         nestedScrollWebView.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.
             // The deprecated `shouldOverrideUrlLoading` must be used until API >= 24.
             @Override
             public boolean shouldOverrideUrlLoading(WebView view, String url) {
                 // Sanitize the url.
                 url = sanitizeUrl(url);
 
+                // Handle the URL according to the type.
                 if (url.startsWith("http")) {  // Load the URL in Privacy Browser.
-                    // Apply the domain settings for the new URL.  This doesn't do anything if the domain has not changed.
-                    boolean userAgentChanged = applyDomainSettings(nestedScrollWebView, url, true, false);
-
-                    // Check if the user agent has changed.
-                    if (userAgentChanged) {
-                        // Manually load the URL.  The changing of the user agent will cause WebView to reload the previous URL.
-                        nestedScrollWebView.loadUrl(url, customHeaders);
-
-                        // Returning true indicates that Privacy Browser is manually handling the loading of the URL.
-                        return true;
-                    } else {
-                        // Returning false causes the current WebView to handle the URL and prevents it from adding redirects to the history list.
-                        return false;
-                    }
+                    // Load the URL.  By using `loadUrl()`, instead of `loadUrlFromBase()`, the Referer header will never be sent.
+                    loadUrl(nestedScrollWebView, url);
+
+                    // Returning true indicates that Privacy Browser is manually handling the loading of the URL.
+                    // Custom headers cannot be added if false is returned and the WebView handles the loading of the URL.
+                    return true;
                 } else if (url.startsWith("mailto:")) {  // Load the email address in an external email program.
                     // Use `ACTION_SENDTO` instead of `ACTION_SEND` so that only email programs are launched.
                     Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
@@ -5482,8 +5762,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // Open the email program in a new task instead of as part of Privacy Browser.
                     emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-                    // Make it so.
-                    startActivity(emailIntent);
+                    try {
+                        // Make it so.
+                        startActivity(emailIntent);
+                    } catch (ActivityNotFoundException exception) {
+                        // Display a snackbar.
+                        Snackbar.make(currentWebView, getString(R.string.error) + "  " + exception, Snackbar.LENGTH_INDEFINITE).show();
+                    }
+
 
                     // Returning true indicates Privacy Browser is handling the URL by creating an intent.
                     return true;
@@ -5497,8 +5783,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // Open the dialer in a new task instead of as part of Privacy Browser.
                     dialIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-                    // Make it so.
-                    startActivity(dialIntent);
+                    try {
+                        // Make it so.
+                        startActivity(dialIntent);
+                    } catch (ActivityNotFoundException exception) {
+                        // Display a snackbar.
+                        Snackbar.make(currentWebView, getString(R.string.error) + "  " + exception, Snackbar.LENGTH_INDEFINITE).show();
+                    }
 
                     // Returning true indicates Privacy Browser is handling the URL by creating an intent.
                     return true;
@@ -5552,15 +5843,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Sanitize the URL.
                 url = sanitizeUrl(url);
 
-                // Get a handle for the navigation view.
-                NavigationView navigationView = findViewById(R.id.navigationview);
-
-                // Get a handle for the navigation menu.
-                Menu navigationMenu = navigationView.getMenu();
-
-                // Get a handle for the navigation requests menu item.  The menu is 0 based.
-                MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
-
                 // Create an empty web resource response to be used if the resource request is blocked.
                 WebResourceResponse emptyWebResourceResponse = new WebResourceResponse("text/plain", "utf8", new ByteArrayInputStream("".getBytes()));
 
@@ -5627,8 +5909,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                             // Update the options menu if it has been populated.
                             if (optionsMenu != null) {
-                                optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
-                                optionsMenu.findItem(R.id.block_all_third_party_requests).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.THIRD_PARTY_REQUESTS) + " - " +
+                                optionsBlocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+                                optionsBlockAllThirdPartyRequestsMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.THIRD_PARTY_REQUESTS) + " - " +
                                         getString(R.string.block_all_third_party_requests));
                             }
                         });
@@ -5661,8 +5943,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                                 // Update the options menu if it has been populated.
                                 if (optionsMenu != null) {
-                                    optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
-                                    optionsMenu.findItem(R.id.ultralist).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.ULTRALIST) + " - " + getString(R.string.ultralist));
+                                    optionsBlocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+                                    optionsUltraListMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.ULTRALIST) + " - " + getString(R.string.ultralist));
                                 }
                             });
                         }
@@ -5702,8 +5984,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                                 // Update the options menu if it has been populated.
                                 if (optionsMenu != null) {
-                                    optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
-                                    optionsMenu.findItem(R.id.ultraprivacy).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.ULTRAPRIVACY) + " - " + getString(R.string.ultraprivacy));
+                                    optionsBlocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+                                    optionsUltraPrivacyMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.ULTRAPRIVACY) + " - " + getString(R.string.ultraprivacy));
                                 }
                             });
                         }
@@ -5743,8 +6025,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                                 // Update the options menu if it has been populated.
                                 if (optionsMenu != null) {
-                                    optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
-                                    optionsMenu.findItem(R.id.easylist).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.EASYLIST) + " - " + getString(R.string.easylist));
+                                    optionsBlocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+                                    optionsEasyListMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.EASYLIST) + " - " + getString(R.string.easylist));
                                 }
                             });
                         }
@@ -5781,8 +6063,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                                 // Update the options menu if it has been populated.
                                 if (optionsMenu != null) {
-                                    optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
-                                    optionsMenu.findItem(R.id.easyprivacy).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.EASYPRIVACY) + " - " + getString(R.string.easyprivacy));
+                                    optionsBlocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+                                    optionsEasyPrivacyMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.EASYPRIVACY) + " - " + getString(R.string.easyprivacy));
                                 }
                             });
                         }
@@ -5819,8 +6101,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                                 // Update the options menu if it has been populated.
                                 if (optionsMenu != null) {
-                                    optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
-                                    optionsMenu.findItem(R.id.fanboys_annoyance_list).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST) + " - " +
+                                    optionsBlocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+                                    optionsFanboysAnnoyanceListMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST) + " - " +
                                             getString(R.string.fanboys_annoyance_list));
                                 }
                             });
@@ -5856,8 +6138,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                                 // Update the options menu if it has been populated.
                                 if (optionsMenu != null) {
-                                    optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
-                                    optionsMenu.findItem(R.id.fanboys_social_blocking_list).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST) + " - " +
+                                    optionsBlocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+                                    optionsFanboysSocialBlockingListMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST) + " - " +
                                             getString(R.string.fanboys_social_blocking_list));
                                 }
                             });
@@ -5901,9 +6183,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get the preferences.
                 boolean scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
 
-                // Get a handler for the app bar layout.
-                AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
-
                 // Set the top padding of the swipe refresh layout according to the app bar scrolling preference.  This can't be done in `appAppSettings()` because the app bar is not yet populated there.
                 if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) {
                     // No padding is needed because it will automatically be placed below the app bar layout due to the scrolling layout behavior.
@@ -5928,30 +6207,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Reset the requests counters.
                 nestedScrollWebView.resetRequestsCounters();
 
-                // TODO.  Make the background of a new tab match the theme.
-                // If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
-                if (nestedScrollWebView.getNightMode()) {
-                    nestedScrollWebView.setVisibility(View.INVISIBLE);
-                } else {
-                    nestedScrollWebView.setVisibility(View.VISIBLE);
-                }
-
-                // Hide the keyboard.
-                inputMethodManager.hideSoftInputFromWindow(nestedScrollWebView.getWindowToken(), 0);
-
                 // Get the current page position.
                 int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
 
-                // Update the URL text bar if the page is currently selected.
-                if (tabLayout.getSelectedTabPosition() == currentPagePosition) {
-                    // Clear the focus from the URL edit text.
-                    urlEditText.clearFocus();
-
+                // Update the URL text bar if the page is currently selected and the URL edit text is not currently being edited.
+                if ((tabLayout.getSelectedTabPosition() == currentPagePosition) && !urlEditText.hasFocus()) {
                     // Display the formatted URL text.
                     urlEditText.setText(url);
 
                     // Apply text highlighting to `urlTextBox`.
                     highlightUrlText();
+
+                    // Hide the keyboard.
+                    inputMethodManager.hideSoftInputFromWindow(nestedScrollWebView.getWindowToken(), 0);
                 }
 
                 // Reset the list of host IP addresses.
@@ -5965,14 +6233,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Replace Refresh with Stop if the options menu has been created.  (The first WebView typically begins loading before the menu items are instantiated.)
                 if (optionsMenu != null) {
-                    // Get a handle for the refresh menu item.
-                    MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh);
-
                     // Set the title.
-                    refreshMenuItem.setTitle(R.string.stop);
+                    optionsRefreshMenuItem.setTitle(R.string.stop);
 
                     // Get the app bar and theme preferences.
-                    boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+                    boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean(getString(R.string.display_additional_app_bar_icons_key), false);
 
                     // If the icon is displayed in the AppBar, set it according to the theme.
                     if (displayAdditionalAppBarIcons) {
@@ -5980,10 +6245,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
 
                         // Set the stop icon according to the theme.
-                        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                            refreshMenuItem.setIcon(R.drawable.close_night);
+                        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+                            optionsRefreshMenuItem.setIcon(R.drawable.close_blue_day);
                         } else {
-                            refreshMenuItem.setIcon(R.drawable.close_day);
+                            optionsRefreshMenuItem.setIcon(R.drawable.close_blue_night);
                         }
                     }
                 }
@@ -5998,14 +6263,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Update the Refresh menu item if the options menu has been created.
                 if (optionsMenu != null) {
-                    // Get a handle for the refresh menu item.
-                    MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh);
-
                     // Reset the Refresh title.
-                    refreshMenuItem.setTitle(R.string.refresh);
+                    optionsRefreshMenuItem.setTitle(R.string.refresh);
 
                     // Get the app bar and theme preferences.
-                    boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+                    boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean(getString(R.string.display_additional_app_bar_icons_key), false);
 
                     // If the icon is displayed in the app bar, reset it according to the theme.
                     if (displayAdditionalAppBarIcons) {
@@ -6013,15 +6275,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
 
                         // Set the icon according to the theme.
-                        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                            refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
+                        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+                            optionsRefreshMenuItem.setIcon(R.drawable.refresh_enabled_day);
                         } else {
-                            refreshMenuItem.setIcon(R.drawable.refresh_enabled_day);
+                            optionsRefreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
                         }
                     }
                 }
 
-                // Clear the cache and history if Incognito Mode is enabled.
+                // Clear the cache, history, and logcat if Incognito Mode is enabled.
                 if (incognitoModeEnabled) {
                     // Clear the cache.  `true` includes disk files.
                     nestedScrollWebView.clearCache(true);
@@ -6041,20 +6303,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         // Delete the secondary `Service Worker` cache directory.
                         // A `String[]` must be used because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise.
                         Runtime.getRuntime().exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"});
-                    } catch (IOException e) {
+                    } catch (IOException exception) {
                         // Do nothing if an error is thrown.
                     }
+
+                    // Clear the logcat.
+                    try {
+                        // Clear the logcat.  `-c` clears the logcat.  `-b all` clears all the buffers (instead of just crash, main, and system).
+                        Runtime.getRuntime().exec("logcat -b all -c");
+                    } catch (IOException exception) {
+                        // Do nothing.
+                    }
                 }
 
                 // Get the current page position.
                 int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
 
-                // Check the current website information against any pinned domain information if the current IP addresses have been loaded.
-                if ((nestedScrollWebView.hasPinnedSslCertificate() || nestedScrollWebView.hasPinnedIpAddresses()) && nestedScrollWebView.hasCurrentIpAddresses() &&
-                        !nestedScrollWebView.ignorePinnedDomainInformation()) {
-                    CheckPinnedMismatchHelper.checkPinnedMismatch(getSupportFragmentManager(), nestedScrollWebView);
-                }
-
                 // Get the current URL from the nested scroll WebView.  This is more accurate than using the URL passed into the method, which is sometimes not the final one.
                 String currentUrl = nestedScrollWebView.getUrl();
 
@@ -6077,7 +6341,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         inputMethodManager.showSoftInput(urlEditText, 0);
 
                         // Apply the domain settings.  This clears any settings from the previous domain.
-                        applyDomainSettings(nestedScrollWebView, "", true, false);
+                        applyDomainSettings(nestedScrollWebView, "", true, false, false);
 
                         // Only populate the title text view if the tab has been fully created.
                         if (tab != null) {
@@ -6171,23 +6435,27 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
         });
 
-        // Check to see if this is the first page.
-        if (pageNumber == 0) {
+        // Check to see if the state is being restored.
+        if (restoringState) {  // The state is being restored.
+            // Resume the nested scroll WebView JavaScript timers.
+            nestedScrollWebView.resumeTimers();
+        } else if (pageNumber == 0) {  // The first page is being loaded.
             // Set this nested scroll WebView as the current WebView.
             currentWebView = nestedScrollWebView;
 
-            // Apply the app settings from the shared preferences.
-            applyAppSettings();
-
             // Initialize the URL to load string.
             String urlToLoadString;
 
             // Get the intent that started the app.
             Intent launchingIntent = getIntent();
 
+            // Reset the intent.  This prevents a duplicate tab from being created on restart.
+            setIntent(new Intent());
+
             // Get the information from the intent.
             String launchingIntentAction = launchingIntent.getAction();
             Uri launchingIntentUriData = launchingIntent.getData();
+            String launchingIntentStringExtra = launchingIntent.getStringExtra(Intent.EXTRA_TEXT);
 
             // Parse the launching intent URL.
             if ((launchingIntentAction != null) && launchingIntentAction.equals(Intent.ACTION_WEB_SEARCH)) {  // The intent contains a search string.
@@ -6203,9 +6471,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Store the web search as the URL to load.
                 urlToLoadString = searchURL + encodedUrlString;
-            } else if (launchingIntentUriData != null){  // The intent contains a URL.
-                // Store the URL.
+            } else if (launchingIntentUriData != null) {  // The launching intent contains a URL formatted as a URI.
+                // Store the URI as a URL.
                 urlToLoadString = launchingIntentUriData.toString();
+            } else if (launchingIntentStringExtra != null) {  // The launching intent contains text that might be a URL.
+                // Store the URL.
+                urlToLoadString = launchingIntentStringExtra;
+            } else if (!url.equals("")) {  // The activity has been restarted.
+                // Load the saved URL.
+                urlToLoadString = url;
             } else {  // The is no URL in the intent.
                 // Store the homepage to be loaded.
                 urlToLoadString = sharedPreferences.getString("homepage", getString(R.string.homepage_default_value));
@@ -6217,12 +6491,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             } else {  // Load the URL.
                 loadUrl(nestedScrollWebView, urlToLoadString);
             }
-        } else {  // This is not the first tab.
-            // Apply the domain settings.
-            applyDomainSettings(nestedScrollWebView, url, false, false);
 
+            // Reset the intent.  This prevents a duplicate tab from being created on a subsequent restart if loading an link from a new intent on restart.
+            // For example, this prevents a duplicate tab if a link is loaded from the Guide after changing the theme in the guide and then changing the theme again in the main activity.
+            setIntent(new Intent());
+        } else {  // This is not the first tab.
             // Load the URL.
-            nestedScrollWebView.loadUrl(url, customHeaders);
+            loadUrl(nestedScrollWebView, url);
 
             // Set the focus and display the keyboard if the URL is blank.
             if (url.equals("")) {