]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
Add night mode controls to the options menu. https://redmine.stoutner.com/issues/274
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / MainWebViewActivity.java
index 933de06a1a5afe846a4bc574f6dc025e2dc61812..115adcc2ca710f0881c90213658a903dc105efbe 100644 (file)
@@ -23,6 +23,7 @@ package com.stoutner.privacybrowser.activities;
 
 import android.Manifest;
 import android.annotation.SuppressLint;
+import android.app.Activity;
 import android.app.DialogFragment;
 import android.app.DownloadManager;
 import android.content.ActivityNotFoundException;
@@ -154,10 +155,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         HttpAuthenticationDialog.HttpAuthenticationListener, NavigationView.OnNavigationItemSelectedListener, PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener,
         SslCertificateErrorDialog.SslCertificateErrorListener, UrlHistoryDialog.UrlHistoryListener {
 
-    // `darkTheme` is public static so it can be accessed from `AboutActivity`, `GuideActivity`, `AddDomainDialog`, `SettingsActivity`, `DomainsActivity`, `DomainsListFragment`, `BookmarksActivity`,
-    // `BookmarksDatabaseViewActivity`, `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `DownloadFileDialog`, `DownloadImageDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`,
-    // `EditBookmarkDatabaseViewDialog`, `HttpAuthenticationDialog`, `MoveToFolderDialog`, `SslCertificateErrorDialog`, `UrlHistoryDialog`, `ViewSslCertificateDialog`, `CreateHomeScreenShortcutDialog`,
-    //  and `OrbotProxyHelper`. It is also used in `onCreate()`, `applyAppSettings()`, `applyDomainSettings()`, and `updatePrivacyIcons()`.
+    // `darkTheme` is public static so it can be accessed from everywhere.
     public static boolean darkTheme;
 
     // `allowScreenshots` is public static so it can be accessed from everywhere.  It is also used in `onCreate()`.
@@ -176,7 +174,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // and `ViewSslCertificateDialog`.  It is also used in `onCreate()`.
     public static SslCertificate sslCertificate;
 
-    // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`.  It is also used in `onCreate()`.
+    // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`.  It is also used in `onCreate()` and `onResume()`.
     public static String orbotStatus;
 
     // `webViewTitle` is public static so it can be accessed from `CreateBookmarkDialog` and `CreateHomeScreenShortcutDialog`.  It is also used in `onCreate()`.
@@ -197,8 +195,60 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // The block list versions are public static so they can be accessed from `AboutTabFragment`.  They are also used in `onCreate()`.
     public static String easyListVersion;
     public static String easyPrivacyVersion;
-    public static String fanboyAnnoyanceVersion;
-    public static String fanboySocialVersion;
+    public static String fanboysAnnoyanceVersion;
+    public static String fanboysSocialVersion;
+    public static String ultraPrivacyVersion;
+
+    // The request items are public static so they can be accessed by `BlockListHelper`, `RequestsArrayAdapter`, and `ViewRequestsDialog`.  They are also used in `onCreate()` and `onPrepareOptionsMenu()`.
+    public static List<String[]> resourceRequests;
+    public static String[] whiteListResultStringArray;
+    int blockedRequests;
+    int easyListBlockedRequests;
+    int easyPrivacyBlockedRequests;
+    int fanboysAnnoyanceListBlockedRequests;
+    int fanboysSocialBlockingListBlockedRequests;
+    int ultraPrivacyBlockedRequests;
+    int thirdPartyBlockedRequests;
+
+    public final static int REQUEST_DISPOSITION = 0;
+    public final static int REQUEST_URL = 1;
+    public final static int REQUEST_BLOCKLIST = 2;
+    public final static int REQUEST_SUBLIST = 3;
+    public final static int REQUEST_BLOCKLIST_ENTRIES = 4;
+    public final static int REQUEST_BLOCKLIST_ORIGINAL_ENTRY = 5;
+
+    public final static int REQUEST_DEFAULT = 0;
+    public final static int REQUEST_ALLOWED = 1;
+    public final static int REQUEST_THIRD_PARTY = 2;
+    public final static int REQUEST_BLOCKED = 3;
+
+    public final static int MAIN_WHITELIST = 1;
+    public final static int FINAL_WHITELIST = 2;
+    public final static int DOMAIN_WHITELIST = 3;
+    public final static int DOMAIN_INITIAL_WHITELIST = 4;
+    public final static int DOMAIN_FINAL_WHITELIST = 5;
+    public final static int THIRD_PARTY_WHITELIST = 6;
+    public final static int THIRD_PARTY_DOMAIN_WHITELIST = 7;
+    public final static int THIRD_PARTY_DOMAIN_INITIAL_WHITELIST = 8;
+
+    public final static int MAIN_BLACKLIST = 9;
+    public final static int INITIAL_BLACKLIST = 10;
+    public final static int FINAL_BLACKLIST = 11;
+    public final static int DOMAIN_BLACKLIST = 12;
+    public final static int DOMAIN_INITIAL_BLACKLIST = 13;
+    public final static int DOMAIN_FINAL_BLACKLIST = 14;
+    public final static int DOMAIN_REGULAR_EXPRESSION_BLACKLIST = 15;
+    public final static int THIRD_PARTY_BLACKLIST = 16;
+    public final static int THIRD_PARTY_INITIAL_BLACKLIST = 17;
+    public final static int THIRD_PARTY_DOMAIN_BLACKLIST = 18;
+    public final static int THIRD_PARTY_DOMAIN_INITIAL_BLACKLIST = 19;
+    public final static int THIRD_PARTY_REGULAR_EXPRESSION_BLACKLIST = 20;
+    public final static int THIRD_PARTY_DOMAIN_REGULAR_EXPRESSION_BLACKLIST = 21;
+    public final static int REGULAR_EXPRESSION_BLACKLIST = 22;
+
+    // `blockAllThirdPartyRequests` is public static so it can be accessed from `RequestsActivity`.
+    // It is also used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`
+    public static boolean blockAllThirdPartyRequests;
 
     // `currentBookmarksFolder` is public static so it can be accessed from `BookmarksActivity`.  It is also used in `onCreate()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`,
     // `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
@@ -248,7 +298,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `fullScreenVideoFrameLayout` is used in `onCreate()` and `onConfigurationChanged()`.
     private FrameLayout fullScreenVideoFrameLayout;
 
-    // `swipeRefreshLayout` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsMenuSelected()`, and `onRestart()`.
+    // `swipeRefreshLayout` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `onRestart()`.
     private SwipeRefreshLayout swipeRefreshLayout;
 
     // `urlAppBarRelativeLayout` is used in `onCreate()` and `applyDomainSettings()`.
@@ -278,7 +328,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `saveFormDataEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.  It can be removed once the minimum API >= 26.
     private boolean saveFormDataEnabled;
 
-    // `nightMode` is used in `onCreate()` and  `applyDomainSettings()`.
+    // `nightMode` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and  `applyDomainSettings()`.
     private boolean nightMode;
 
     // `displayWebpageImagesBoolean` is used in `applyAppSettings()` and `applyDomainSettings()`.
@@ -290,11 +340,33 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `searchURL` is used in `loadURLFromTextBox()` and `applyAppSettings()`.
     private String searchURL;
 
-    // The block list variables are used in `onCreate()` and `applyAppSettings()`.
+    // `mainMenu` is used in `onCreateOptionsMenu()` and `updatePrivacyIcons()`.
+    private Menu mainMenu;
+
+    // `refreshMenuItem` is used in `onCreate()` and `onCreateOptionsMenu()`.
+    private MenuItem refreshMenuItem;
+
+    // The blocklist menu items are used in `onCreate()`, `onCreateOptionsMenu()`, and `onPrepareOptionsMenu()`.
+    private MenuItem blocklistsMenuItem;
+    private MenuItem easyListMenuItem;
+    private MenuItem easyPrivacyMenuItem;
+    private MenuItem fanboysAnnoyanceListMenuItem;
+    private MenuItem fanboysSocialBlockingListMenuItem;
+    private MenuItem ultraPrivacyMenuItem;
+    private MenuItem blockAllThirdParyRequestsMenuItem;
+
+    // The blocklist variables are used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`.
     private boolean easyListEnabled;
     private boolean easyPrivacyEnabled;
     private boolean fanboysAnnoyanceListEnabled;
     private boolean fanboysSocialBlockingListEnabled;
+    private boolean ultraPrivacyEnabled;
+
+    // `webViewDefaultUserAgent` is used in `onCreate()` and `onPrepareOptionsMenu()`.
+    private String webViewDefaultUserAgent;
+
+    // `defaultCustomUserAgentString` is used in `onPrepareOptionsMenu()` and `applyDomainSettings()`.
+    private String defaultCustomUserAgentString;
 
     // `privacyBrowserRuntime` is used in `onCreate()`, `onOptionsItemSelected()`, and `applyAppSettings()`.
     private Runtime privacyBrowserRuntime;
@@ -323,18 +395,27 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `reapplyAppSettingsOnRestart` is used in `onNavigationItemSelected()` and `onRestart()`.
     private boolean reapplyAppSettingsOnRestart;
 
+    // `displayingFullScreenVideo` is used in `onCreate()` and `onResume()`.
+    private boolean displayingFullScreenVideo;
+
     // `currentDomainName` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onAddDomain()`, and `applyDomainSettings()`.
     private String currentDomainName;
 
     // `ignorePinnedSslCertificateForDomain` is used in `onCreate()`, `onSslMismatchProceed()`, and `applyDomainSettings()`.
     private boolean ignorePinnedSslCertificate;
 
-    // `waitingForOrbot` is used in `onCreate()` and `applyAppSettings()`.
+    // `orbotStatusBroadcastReciever` is used in `onCreate()` and `onDestroy()`.
+    private BroadcastReceiver orbotStatusBroadcastReceiver;
+
+    // `waitingForOrbot` is used in `onCreate()`, `onResume()`, and `applyAppSettings()`.
     private boolean waitingForOrbot;
 
     // `domainSettingsApplied` is used in `prepareOptionsMenu()`, `applyDomainSettings()`, and `setDisplayWebpageImages()`.
     private boolean domainSettingsApplied;
 
+    // `domainSettingsJavaScriptEnabled` is used in `onOptionsItemSelected()` and `applyDomainSettings()`.
+    private Boolean domainSettingsJavaScriptEnabled;
+
     // `displayWebpageImagesInt` is used in `applyDomainSettings()` and `setDisplayWebpageImages()`.
     private int displayWebpageImagesInt;
 
@@ -353,8 +434,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `findOnPageEditText` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`.
     private EditText findOnPageEditText;
 
-    // `mainMenu` is used in `onCreateOptionsMenu()` and `updatePrivacyIcons()`.
-    private Menu mainMenu;
+    // `displayAdditionalAppBarIcons` is used in `onCreate()` and `onCreateOptionsMenu()`.
+    private boolean displayAdditionalAppBarIcons;
 
     // `drawerToggle` is used in `onCreate()`, `onPostCreate()`, `onConfigurationChanged()`, `onNewIntent()`, and `onNavigationItemSelected()`.
     private ActionBarDrawerToggle drawerToggle;
@@ -382,7 +463,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `mainWebViewRelativeLayout` is used in `onCreate()` and `onNavigationItemSelected()`.
     private RelativeLayout mainWebViewRelativeLayout;
 
-    // `urlIsLoading` is used in `onCreate()`, `loadUrl()`, and `applyDomainSettings()`.
+    // `urlIsLoading` is used in `onCreate()`, `onCreateOptionsMenu()`, `loadUrl()`, and `applyDomainSettings()`.
     private boolean urlIsLoading;
 
     // `pinnedDomainSslCertificate` is used in `onCreate()` and `applyDomainSettings()`.
@@ -520,7 +601,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         waitingForOrbot = false;
 
         // Create an Orbot status `BroadcastReceiver`.
-        BroadcastReceiver orbotStatusBroadcastReceiver = new BroadcastReceiver() {
+        orbotStatusBroadcastReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
                 // Store the content of the status message in `orbotStatus`.
@@ -754,6 +835,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         final MenuItem navigationBackMenuItem = navigationMenu.getItem(1);
         final MenuItem navigationForwardMenuItem = navigationMenu.getItem(2);
         final MenuItem navigationHistoryMenuItem = navigationMenu.getItem(3);
+        final MenuItem navigationRequestsMenuItem = navigationMenu.getItem(4);
 
         // Initialize the bookmarks database helper.  `this` specifies the context.  The two `nulls` do not specify the database name or a `CursorFactory`.
         // The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
@@ -816,7 +898,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             return true;
         });
 
-        // The `DrawerListener` allows us to update the Navigation Menu.
+        // The drawer listener is used to update the navigation menu.
         drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
             @Override
             public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
@@ -832,16 +914,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             @Override
             public void onDrawerStateChanged(int newState) {
-                if ((newState == DrawerLayout.STATE_SETTLING) || (newState == DrawerLayout.STATE_DRAGGING)) {  // The drawer is opening or closing.
-                    // Update the `Back`, `Forward`, and `History` menu items.
+                if ((newState == DrawerLayout.STATE_SETTLING) || (newState == DrawerLayout.STATE_DRAGGING)) {  // A drawer is opening or closing.
+                    // Update the back, forward, history, and requests menu items.
                     navigationBackMenuItem.setEnabled(mainWebView.canGoBack());
                     navigationForwardMenuItem.setEnabled(mainWebView.canGoForward());
                     navigationHistoryMenuItem.setEnabled((mainWebView.canGoBack() || mainWebView.canGoForward()));
+                    navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
 
-                    // Hide the keyboard (if displayed) so we can see the navigation menu.  `0` indicates no additional flags.
+                    // Hide the keyboard (if displayed).
                     inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
 
-                    // Clear the focus from `urlTextBox` if it has it.
+                    // Clear the focus from from the URL text box.
                     urlTextBox.clearFocus();
                 }
             }
@@ -928,6 +1011,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Enter full screen video.
             @Override
             public void onShowCustomView(View view, CustomViewCallback callback) {
+                // Set the full screen video flag.
+                displayingFullScreenVideo = true;
+
                 // Pause the ad if this is the free flavor.
                 if (BuildConfig.FLAVOR.contentEquals("free")) {
                     // The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
@@ -949,6 +1035,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Set `rootCoordinatorLayout` to fill the entire screen.
                 rootCoordinatorLayout.setFitsSystemWindows(false);
 
+                // Disable the sliding drawers.
+                drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
+
                 // Add `view` to `fullScreenVideoFrameLayout` and display it on the screen.
                 fullScreenVideoFrameLayout.addView(view);
                 fullScreenVideoFrameLayout.setVisibility(View.VISIBLE);
@@ -957,15 +1046,72 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Exit full screen video.
             @Override
             public void onHideCustomView() {
+                // Unset the full screen video flag.
+                displayingFullScreenVideo = false;
+
                 // Hide `fullScreenVideoFrameLayout`.
                 fullScreenVideoFrameLayout.removeAllViews();
                 fullScreenVideoFrameLayout.setVisibility(View.GONE);
 
-                // Add the translucent status flag.  This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`.
-                getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+                // Enable the sliding drawers.
+                drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
+
+                // Apply the appropriate full screen mode the `SYSTEM_UI` flags.
+                if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) {  // Privacy Browser is currently in full screen browsing mode.
+                    if (hideSystemBarsOnFullscreen) {  // Hide everything.
+                        // Remove the translucent navigation setting if it is currently flagged.
+                        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+
+                        // Remove the translucent status bar overlay.
+                        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+
+                        // Remove the translucent status bar overlay on the `Drawer Layout`, which is special and needs its own command.
+                        drawerLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+
+                        /* SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
+                         * 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.
+                         */
+                        rootCoordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+                    } else {  // Hide everything except the status and navigation bars.
+                        // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`.
+                        rootCoordinatorLayout.setSystemUiVisibility(0);
+
+                        // Add the translucent status flag if it is unset.
+                        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+
+                        if (translucentNavigationBarOnFullscreen) {
+                            // Set the navigation bar to be translucent.  This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`.
+                            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+                        } else {
+                            // Set the navigation bar to be black.
+                            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+                        }
+                    }
+                } else {  // Switch to normal viewing mode.
+                    // Show the `appBar` if `findOnPageLinearLayout` is not visible.
+                    if (findOnPageLinearLayout.getVisibility() == View.GONE) {
+                        appBar.show();
+                    }
+
+                    // Show the `BannerAd` in the free flavor.
+                    if (BuildConfig.FLAVOR.contentEquals("free")) {
+                        // Initialize the ad.  The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+                        AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getFragmentManager(), getString(R.string.ad_id));
+                    }
+
+                    // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`.
+                    rootCoordinatorLayout.setSystemUiVisibility(0);
 
-                // Set `rootCoordinatorLayout` to fit inside the status and navigation bars.  This also clears the `SYSTEM_UI` flags.
-                rootCoordinatorLayout.setFitsSystemWindows(true);
+                    // Remove the translucent navigation bar flag if it is set.
+                    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+
+                    // Add the translucent status flag if it is unset.  This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`.
+                    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+
+                    // Constrain `rootCoordinatorLayout` inside the status and navigation bars.
+                    rootCoordinatorLayout.setFitsSystemWindows(true);
+                }
 
                 // Show the ad if this is the free flavor.
                 if (BuildConfig.FLAVOR.contentEquals("free")) {
@@ -1047,7 +1193,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Replace the header that `WebView` creates for `X-Requested-With` with a null value.  The default value is the application ID (com.stoutner.privacybrowser.standard).
         customHeaders.put("X-Requested-With", "");
 
-        // Initialize the default preference values the first time the program is run.  `this` is the context.  `false` keeps this command from resetting any current preferences back to default.
+        // 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);
 
         // Get the intent that started the app.
@@ -1079,6 +1225,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         saveFormDataEnabled = false;  // Form data can be removed once the minimum API >= 26.
         nightMode = false;
 
+        // Store the default user agent.
+        webViewDefaultUserAgent = mainWebView.getSettings().getUserAgentString();
+
         // Initialize the WebView title.
         webViewTitle = getString(R.string.no_title);
 
@@ -1103,17 +1252,25 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Instantiate the block list helper.
         BlockListHelper blockListHelper = new BlockListHelper();
 
+        // Initialize the list of resource requests.
+        resourceRequests = new ArrayList<>();
+
         // Parse the block lists.
         final ArrayList<List<String[]>> easyList = blockListHelper.parseBlockList(getAssets(), "blocklists/easylist.txt");
         final ArrayList<List<String[]>> easyPrivacy = blockListHelper.parseBlockList(getAssets(), "blocklists/easyprivacy.txt");
-        final ArrayList<List<String[]>> fanboyAnnoyance = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-annoyance.txt");
-        final ArrayList<List<String[]>> fanboySocial = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-social.txt");
+        final ArrayList<List<String[]>> fanboysAnnoyanceList = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-annoyance.txt");
+        final ArrayList<List<String[]>> fanboysSocialList = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-social.txt");
+        final ArrayList<List<String[]>> ultraPrivacy = blockListHelper.parseBlockList(getAssets(), "blocklists/ultraprivacy.txt");
 
         // Store the list versions.
         easyListVersion = easyList.get(0).get(0)[0];
         easyPrivacyVersion = easyPrivacy.get(0).get(0)[0];
-        fanboyAnnoyanceVersion = fanboyAnnoyance.get(0).get(0)[0];
-        fanboySocialVersion = fanboySocial.get(0).get(0)[0];
+        fanboysAnnoyanceVersion = fanboysAnnoyanceList.get(0).get(0)[0];
+        fanboysSocialVersion = fanboysSocialList.get(0).get(0)[0];
+        ultraPrivacyVersion = ultraPrivacy.get(0).get(0)[0];
+
+        // Get a handle for the activity.  This is used to update the requests counter while the navigation menu is open.
+        Activity activity = this;
 
         mainWebView.setWebViewClient(new WebViewClient() {
             // `shouldOverrideUrlLoading` makes this `WebView` the default handler for URLs inside the app, so that links are not kicked out to other apps.
@@ -1122,10 +1279,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             @Override
             public boolean shouldOverrideUrlLoading(WebView view, String url) {
                 if (url.startsWith("http")) {  // Load the URL in Privacy Browser.
-                    // Apply the domain settings for the new URL.
+                    // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+                    formattedUrlString = "";
+
+                    // Apply the domain settings for the new URL.  `applyDomainSettings` doesn't do anything if the domain has not changed.
                     applyDomainSettings(url, true, false);
 
-                    // Returning false causes the current `WebView` to handle the URL and prevents it from adding redirects to the history list.
+                    // Returning false causes the current WebView to handle the URL and prevents it from adding redirects to the history list.
                     return false;
                 } 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.
@@ -1182,16 +1342,119 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 }
             }
 
-            // Check requests against the block lists.  The deprecated `shouldInterceptRequest` must be used until minimum API >= 21.
+            // Check requests against the block lists.  The deprecated `shouldInterceptRequest()` must be used until minimum API >= 21.
             @SuppressWarnings("deprecation")
             @Override
             public WebResourceResponse shouldInterceptRequest(WebView view, String url){
                 // 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()));
 
+                // Reset the whitelist results tracker.
+                whiteListResultStringArray = null;
+
+                // Initialize the third party request tracker.
+                boolean isThirdPartyRequest = false;
+
+                // Initialize the current domain string.
+                String currentDomain = "";
+
+                // Nobody is happy when comparing null strings.
+                if (!(formattedUrlString == null) && !(url == null)) {
+                    // Get the domain strings to URIs.
+                    Uri currentDomainUri = Uri.parse(formattedUrlString);
+                    Uri requestDomainUri = Uri.parse(url);
+
+                    // Get the domain host names.
+                    String currentBaseDomain = currentDomainUri.getHost();
+                    String requestBaseDomain = requestDomainUri.getHost();
+
+                    // Update the current domain variable.
+                    currentDomain = currentBaseDomain;
+
+                    // Only compare the current base domain and the request base domain if neither is null.
+                    if (!(currentBaseDomain == null) && !(requestBaseDomain == null)) {
+                        // Determine the current base domain.
+                        while (currentBaseDomain.indexOf(".", currentBaseDomain.indexOf(".") + 1) > 0) {  // There is at least one subdomain.
+                            // Remove the first subdomain.
+                            currentBaseDomain = currentBaseDomain.substring(currentBaseDomain.indexOf(".") + 1);
+                        }
+
+                        // Determine the request base domain.
+                        while (requestBaseDomain.indexOf(".", requestBaseDomain.indexOf(".") + 1) > 0) {  // There is at least one subdomain.
+                            // Remove the first subdomain.
+                            requestBaseDomain = requestBaseDomain.substring(requestBaseDomain.indexOf(".") + 1);
+                        }
+
+                        // Update the third party request tracker.
+                        isThirdPartyRequest = !currentBaseDomain.equals(requestBaseDomain);
+                    }
+                }
+
+                // Block third-party requests if enabled.
+                if (isThirdPartyRequest && blockAllThirdPartyRequests) {
+                    // Increment the blocked requests counters.
+                    blockedRequests++;
+                    thirdPartyBlockedRequests++;
+
+                    // Update the titles of the blocklist menu items.  This must be run from the UI thread.
+                    activity.runOnUiThread(() -> {
+                        navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+                        blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+                        blockAllThirdParyRequestsMenuItem.setTitle(thirdPartyBlockedRequests + " - " + getString(R.string.block_all_third_party_requests));
+                    });
+
+                    // Add the request to the log.
+                    resourceRequests.add(new String[]{String.valueOf(REQUEST_THIRD_PARTY), url});
+
+                    // Return an empty web resource response.
+                    return emptyWebResourceResponse;
+                }
+
+                // Check UltraPrivacy if it is enabled.
+                if (ultraPrivacyEnabled) {
+                    if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, ultraPrivacy)) {
+                        // Increment the blocked requests counters.
+                        blockedRequests++;
+                        ultraPrivacyBlockedRequests++;
+
+                        // Update the titles of the blocklist menu items.  This must be run from the UI thread.
+                        activity.runOnUiThread(() -> {
+                            navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+                            blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+                            ultraPrivacyMenuItem.setTitle(ultraPrivacyBlockedRequests + " - " + getString(R.string.ultraprivacy));
+                        });
+
+                        // The resource request was blocked.  Return an empty web resource response.
+                        return emptyWebResourceResponse;
+                    }
+
+                    // If the whitelist result is not null, the request has been allowed by UltraPrivacy.
+                    if (whiteListResultStringArray != null) {
+                        // Add a whitelist entry to the resource requests array.
+                        resourceRequests.add(whiteListResultStringArray);
+
+                        // The resource request has been allowed by UltraPrivacy.  `return null` loads the requested resource.
+                        return null;
+                    }
+                }
+
                 // Check EasyList if it is enabled.
                 if (easyListEnabled) {
-                    if (blockListHelper.isBlocked(formattedUrlString, url, easyList)) {
+                    if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, easyList)) {
+                        // Increment the blocked requests counters.
+                        blockedRequests++;
+                        easyListBlockedRequests++;
+
+                        // Update the titles of the blocklist menu items.  This must be run from the UI thread.
+                        activity.runOnUiThread(() -> {
+                            navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+                            blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+                            easyListMenuItem.setTitle(easyListBlockedRequests + " - " + getString(R.string.easylist));
+                        });
+
+                        // Reset the whitelist results tracker (because otherwise it will sometimes add results to the list due to a race condition).
+                        whiteListResultStringArray = null;
+
                         // The resource request was blocked.  Return an empty web resource response.
                         return emptyWebResourceResponse;
                     }
@@ -1199,7 +1462,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Check EasyPrivacy if it is enabled.
                 if (easyPrivacyEnabled) {
-                    if (blockListHelper.isBlocked(formattedUrlString, url, easyPrivacy)) {
+                    if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, easyPrivacy)) {
+                        // Increment the blocked requests counters.
+                        blockedRequests++;
+                        easyPrivacyBlockedRequests++;
+
+                        // Update the titles of the blocklist menu items.  This must be run from the UI thread.
+                        activity.runOnUiThread(() -> {
+                            navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+                            blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+                            easyPrivacyMenuItem.setTitle(easyPrivacyBlockedRequests + " - " + getString(R.string.easyprivacy));
+                        });
+
+                        // Reset the whitelist results tracker (because otherwise it will sometimes add results to the list due to a race condition).
+                        whiteListResultStringArray = null;
+
                         // The resource request was blocked.  Return an empty web resource response.
                         return emptyWebResourceResponse;
                     }
@@ -1207,17 +1484,52 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Check Fanboy’s Annoyance List if it is enabled.
                 if (fanboysAnnoyanceListEnabled) {
-                    if (blockListHelper.isBlocked(formattedUrlString, url, fanboyAnnoyance)) {
+                    if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, fanboysAnnoyanceList)) {
+                        // Increment the blocked requests counters.
+                        blockedRequests++;
+                        fanboysAnnoyanceListBlockedRequests++;
+
+                        // Update the titles of the blocklist menu items.  This must be run from the UI thread.
+                        activity.runOnUiThread(() -> {
+                            navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+                            blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+                            fanboysAnnoyanceListMenuItem.setTitle(fanboysAnnoyanceListBlockedRequests + " - " + getString(R.string.fanboys_annoyance_list));
+                        });
+
+                        // Reset the whitelist results tracker (because otherwise it will sometimes add results to the list due to a race condition).
+                        whiteListResultStringArray = null;
+
                         // The resource request was blocked.  Return an empty web resource response.
                         return emptyWebResourceResponse;
                     }
                 } else if (fanboysSocialBlockingListEnabled){  // Only check Fanboy’s Social Blocking List if Fanboy’s Annoyance List is disabled.
-                    if (blockListHelper.isBlocked(formattedUrlString, url, fanboySocial)) {
+                    if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, fanboysSocialList)) {
+                        // Increment the blocked requests counters.
+                        blockedRequests++;
+                        fanboysSocialBlockingListBlockedRequests++;
+
+                        // Update the titles of the blocklist menu items.  This must be run from the UI thread.
+                        activity.runOnUiThread(() -> {
+                            navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+                            blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+                            fanboysSocialBlockingListMenuItem.setTitle(fanboysSocialBlockingListBlockedRequests + " - " + getString(R.string.fanboys_social_blocking_list));
+                        });
+
+                        // Reset the whitelist results tracker (because otherwise it will sometimes add results to the list due to a race condition).
+                        whiteListResultStringArray = null;
+
                         // The resource request was blocked.  Return an empty web resource response.
                         return emptyWebResourceResponse;
                     }
                 }
 
+                // Add the request to the log because it hasn't been processed by any of the previous checks.
+                if (whiteListResultStringArray != null ) {  // The request was processed by a whitelist.
+                    resourceRequests.add(whiteListResultStringArray);
+                } else {  // The request didn't match any blocklist entry.  Log it as a defult request.
+                    resourceRequests.add(new String[]{String.valueOf(REQUEST_DEFAULT), url});
+                }
+
                 // The resource request has not been blocked.  `return null` loads the requested resource.
                 return null;
             }
@@ -1235,7 +1547,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             // Update the URL in urlTextBox when the page starts to load.
             @Override
-            public void onPageStarted(WebView view, String url, Bitmap favicon) {// If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
+            public void onPageStarted(WebView view, String url, Bitmap favicon) {
+                // Reset the list of resource requests.
+                resourceRequests.clear();
+
+                // Initialize the counters for requests blocked by each blocklist.
+                blockedRequests = 0;
+                easyListBlockedRequests = 0;
+                easyPrivacyBlockedRequests = 0;
+                fanboysAnnoyanceListBlockedRequests = 0;
+                fanboysSocialBlockingListBlockedRequests = 0;
+                ultraPrivacyBlockedRequests = 0;
+                thirdPartyBlockedRequests = 0;
+
+                // If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
                 if (nightMode) {
                     mainWebView.setVisibility(View.INVISIBLE);
                 }
@@ -1243,7 +1568,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Hide the keyboard.  `0` indicates no additional flags.
                 inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
 
-                // Check to see if we are waiting on Orbot.
+                // Check to see if Privacy Browser is waiting on Orbot.
                 if (!waitingForOrbot) {  // We are not waiting on Orbot, so we need to process the URL.
                     // We need to update `formattedUrlString` at the beginning of the load, so that if the user toggles JavaScript during the load the new website is reloaded.
                     formattedUrlString = url;
@@ -1256,11 +1581,30 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                     // Apply any custom domain settings if the URL was loaded by navigating history.
                     if (navigatingHistory) {
+                        // Reset `navigatingHistory`.
+                        navigatingHistory = false;
+
+                        // Apply the domain settings.
                         applyDomainSettings(url, true, false);
                     }
 
                     // Set `urlIsLoading` to `true`, so that redirects while loading do not trigger changes in the user agent, which forces another reload of the existing page.
                     urlIsLoading = true;
+
+                    // Replace Refresh with Stop if the menu item has been created.  (The WebView typically begins loading before the menu items are instantiated.)
+                    if (refreshMenuItem != null) {
+                        // Set the title.
+                        refreshMenuItem.setTitle(R.string.stop);
+
+                        // If the icon is displayed in the AppBar, set it according to the theme.
+                        if (displayAdditionalAppBarIcons) {
+                            if (darkTheme) {
+                                refreshMenuItem.setIcon(R.drawable.close_dark);
+                            } else {
+                                refreshMenuItem.setIcon(R.drawable.close_light);
+                            }
+                        }
+                    }
                 }
             }
 
@@ -1272,6 +1616,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     cookieManager.flush();
                 }
 
+                // Update the Refresh menu item if it has been created.
+                if (refreshMenuItem != null) {
+                    // Reset the Refresh title.
+                    refreshMenuItem.setTitle(R.string.refresh);
+
+                    // If the icon is displayed in the AppBar, reset it according to the theme.
+                    if (displayAdditionalAppBarIcons) {
+                        if (darkTheme) {
+                            refreshMenuItem.setIcon(R.drawable.refresh_enabled_dark);
+                        } else {
+                            refreshMenuItem.setIcon(R.drawable.refresh_enabled_light);
+                        }
+                    }
+                }
+
                 // Reset `urlIsLoading`, which is used to prevent reloads on redirect if the user agent changes.
                 urlIsLoading = false;
 
@@ -1441,12 +1800,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         // Check to see if the intent contains a new URL.
         if (intent.getData() != null) {
-            // Get the intent data and convert it to a string.
+            // Get the intent data.
             final Uri intentUriData = intent.getData();
-            formattedUrlString = intentUriData.toString();
 
             // Load the website.
-            loadUrl(formattedUrlString);
+            loadUrl(intentUriData.toString());
 
             // Close the navigation drawer if it is open.
             if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
@@ -1546,26 +1904,56 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         // Resume the adView for the free flavor.
         if (BuildConfig.FLAVOR.contentEquals("free")) {
-            // The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+            // Resume the ad.
             AdHelper.resumeAd(findViewById(R.id.adview));
         }
+
+        // Display a message to the user if waiting for Orbot.
+        if (waitingForOrbot && !orbotStatus.equals("ON")) {
+            // Load a waiting page.  `null` specifies no encoding, which defaults to ASCII.
+            mainWebView.loadData(waitingForOrbotHTMLString, "text/html", null);
+        }
+
+        if (displayingFullScreenVideo) {
+            // Remove the translucent overlays.
+            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+
+            // Remove the translucent status bar overlay on the `Drawer Layout`, which is special and needs its own command.
+            drawerLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+
+            /* SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
+             * 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.
+             */
+            rootCoordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+        }
     }
 
     @Override
     public void onPause() {
+        // Run the default commands.
+        super.onPause();
+
         // Pause `mainWebView`.
         mainWebView.onPause();
 
         // Stop all JavaScript.
         mainWebView.pauseTimers();
 
-        // Pause the adView or it will continue to consume resources in the background on the free flavor.
+        // Pause the ad or it will continue to consume resources in the background on the free flavor.
         if (BuildConfig.FLAVOR.contentEquals("free")) {
-            // The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+            // Pause the ad.
             AdHelper.pauseAd(findViewById(R.id.adview));
         }
+    }
 
-        super.onPause();
+    @Override
+    public void onDestroy() {
+        // Unregister the Orbot status broadcast receiver.
+        this.unregisterReceiver(orbotStatusBroadcastReceiver);
+
+        // Run the default commands.
+        super.onDestroy();
     }
 
     @Override
@@ -1585,7 +1973,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         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);
+        refreshMenuItem = menu.findItem(R.id.refresh);
+        blocklistsMenuItem = menu.findItem(R.id.blocklists);
+        easyListMenuItem = menu.findItem(R.id.easylist);
+        easyPrivacyMenuItem = menu.findItem(R.id.easyprivacy);
+        fanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list);
+        fanboysSocialBlockingListMenuItem = menu.findItem(R.id.fanboys_social_blocking_list);
+        ultraPrivacyMenuItem = menu.findItem(R.id.ultraprivacy);
+        blockAllThirdParyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests);
         MenuItem adConsentMenuItem = menu.findItem(R.id.ad_consent);
 
         // Only display third-party cookies if API >= 21
@@ -1598,11 +1993,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Only show Ad Consent if this is the free flavor.
         adConsentMenuItem.setVisible(BuildConfig.FLAVOR.contentEquals("free"));
 
-        // Get the shared preference values.  `this` references the current context.
+        // Get the shared preference values.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
+        // Get the status of the additional AppBar icons.
+        displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", 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 (sharedPreferences.getBoolean("display_additional_app_bar_icons", false)) {
+        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);
@@ -1612,6 +2010,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
         }
 
+        // Replace Refresh with Stop if a URL is already loading.
+        if (urlIsLoading) {
+            // Set the title.
+            refreshMenuItem.setTitle(R.string.stop);
+
+            // If the icon is displayed in the AppBar, set it according to the theme.
+            if (displayAdditionalAppBarIcons) {
+                if (darkTheme) {
+                    refreshMenuItem.setIcon(R.drawable.close_dark);
+                } else {
+                    refreshMenuItem.setIcon(R.drawable.close_light);
+                }
+            }
+        }
+
         return true;
     }
 
@@ -1630,6 +2043,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         MenuItem fontSizeMenuItem = menu.findItem(R.id.font_size);
         MenuItem swipeToRefreshMenuItem = menu.findItem(R.id.swipe_to_refresh);
         MenuItem displayImagesMenuItem = menu.findItem(R.id.display_images);
+        MenuItem nightModeMenuItem = menu.findItem(R.id.night_mode);
 
         // Set the text for the domain menu item.
         if (domainSettingsApplied) {
@@ -1643,44 +2057,98 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         toggleThirdPartyCookiesMenuItem.setChecked(thirdPartyCookiesEnabled);
         toggleDomStorageMenuItem.setChecked(domStorageEnabled);
         toggleSaveFormDataMenuItem.setChecked(saveFormDataEnabled);  // Form data can be removed once the minimum API >= 26.
+        easyListMenuItem.setChecked(easyListEnabled);
+        easyPrivacyMenuItem.setChecked(easyPrivacyEnabled);
+        fanboysAnnoyanceListMenuItem.setChecked(fanboysAnnoyanceListEnabled);
+        fanboysSocialBlockingListMenuItem.setChecked(fanboysSocialBlockingListEnabled);
+        ultraPrivacyMenuItem.setChecked(ultraPrivacyEnabled);
+        blockAllThirdParyRequestsMenuItem.setChecked(blockAllThirdPartyRequests);
         swipeToRefreshMenuItem.setChecked(swipeRefreshLayout.isEnabled());
         displayImagesMenuItem.setChecked(mainWebView.getSettings().getLoadsImagesAutomatically());
+        nightModeMenuItem.setChecked(nightMode);
 
         // Enable third-party cookies if first-party cookies are enabled.
         toggleThirdPartyCookiesMenuItem.setEnabled(firstPartyCookiesEnabled);
 
-        // Enable `DOM Storage` if JavaScript is enabled.
+        // Enable DOM Storage if JavaScript is enabled.
         toggleDomStorageMenuItem.setEnabled(javaScriptEnabled);
 
-        // Enable `Clear Cookies` if there are any.
+        // Enable Clear Cookies if there are any.
         clearCookiesMenuItem.setEnabled(cookieManager.hasCookies());
 
-        // Get a count of the number of files in the `Local Storage` directory.
+        // Get a count of the number of files in the Local Storage directory.
         File localStorageDirectory = new File (privateDataDirectoryString + "/app_webview/Local Storage/");
         int localStorageDirectoryNumberOfFiles = 0;
         if (localStorageDirectory.exists()) {
             localStorageDirectoryNumberOfFiles = localStorageDirectory.list().length;
         }
 
-        // Get a count of the number of files in the `IndexedDB` directory.
+        // Get a count of the number of files in the IndexedDB directory.
         File indexedDBDirectory = new File (privateDataDirectoryString + "/app_webview/IndexedDB");
         int indexedDBDirectoryNumberOfFiles = 0;
         if (indexedDBDirectory.exists()) {
             indexedDBDirectoryNumberOfFiles = indexedDBDirectory.list().length;
         }
 
-        // Enable `Clear DOM Storage` if there is any.
+        // Enable Clear DOM Storage if there is any.
         clearDOMStorageMenuItem.setEnabled(localStorageDirectoryNumberOfFiles > 0 || indexedDBDirectoryNumberOfFiles > 0);
 
-        // Enable `Clear Form Data` is there is any.  This can be removed once the minimum API >= 26.
+        // Enable Clear Form Data is there is any.  This can be removed once the minimum API >= 26.
         if (Build.VERSION.SDK_INT < 26) {
             WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this);
             clearFormDataMenuItem.setEnabled(mainWebViewDatabase.hasFormData());
+        } else {
+            // Disable clear form data because it is not supported on current version of Android.
+            clearFormDataMenuItem.setEnabled(false);
         }
 
-        // Enable `Clear Data` if any of the submenu items are enabled.
+        // Enable Clear Data if any of the submenu items are enabled.
         clearDataMenuItem.setEnabled(clearCookiesMenuItem.isEnabled() || clearDOMStorageMenuItem.isEnabled() || clearFormDataMenuItem.isEnabled());
 
+        // Disable Fanboy's Social Blocking List if Fanboy's Annoyance List is checked.
+        fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListEnabled);
+
+        // Initialize the display names for the blocklists with the number of blocked requests.
+        blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + blockedRequests);
+        easyListMenuItem.setTitle(easyListBlockedRequests + " - " + getString(R.string.easylist));
+        easyPrivacyMenuItem.setTitle(easyPrivacyBlockedRequests + " - " + getString(R.string.easyprivacy));
+        fanboysAnnoyanceListMenuItem.setTitle(fanboysAnnoyanceListBlockedRequests + " - " + getString(R.string.fanboys_annoyance_list));
+        fanboysSocialBlockingListMenuItem.setTitle(fanboysSocialBlockingListBlockedRequests + " - " + getString(R.string.fanboys_social_blocking_list));
+        ultraPrivacyMenuItem.setTitle(ultraPrivacyBlockedRequests + " - " + getString(R.string.ultraprivacy));
+        blockAllThirdParyRequestsMenuItem.setTitle(thirdPartyBlockedRequests + " - " + getString(R.string.block_all_third_party_requests));
+
+        // Get the current user agent.
+        String currentUserAgent = mainWebView.getSettings().getUserAgentString();
+
+        // 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.
+            menu.findItem(R.id.user_agent_privacy_browser).setChecked(true);
+        } else if (currentUserAgent.equals(webViewDefaultUserAgent)) {  // WebView Default.
+            menu.findItem(R.id.user_agent_webview_default).setChecked(true);
+        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[2])) {  // Firefox on Android.
+            menu.findItem(R.id.user_agent_firefox_on_android).setChecked(true);
+        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[3])) {  // Chrome on Android.
+            menu.findItem(R.id.user_agent_chrome_on_android).setChecked(true);
+        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[4])) {  // Safari on iOS.
+            menu.findItem(R.id.user_agent_safari_on_ios).setChecked(true);
+        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[5])) {  // Firefox on Linux.
+            menu.findItem(R.id.user_agent_firefox_on_linux).setChecked(true);
+        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[6])) {  // Chromium on Linux.
+            menu.findItem(R.id.user_agent_chromium_on_linux).setChecked(true);
+        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[7])) {  // Firefox on Windows.
+            menu.findItem(R.id.user_agent_firefox_on_windows).setChecked(true);
+        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[8])) {  // Chrome on Windows.
+            menu.findItem(R.id.user_agent_chrome_on_windows).setChecked(true);
+        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[9])) {  // Edge on Windows.
+            menu.findItem(R.id.user_agent_edge_on_windows).setChecked(true);
+        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[10])) {  // Internet Explorer on Windows.
+            menu.findItem(R.id.user_agent_internet_explorer_on_windows).setChecked(true);
+        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[11])) {  // Safari on macOS.
+            menu.findItem(R.id.user_agent_safari_on_macos).setChecked(true);
+        } else {  // Custom user agent.
+            menu.findItem(R.id.user_agent_custom).setChecked(true);
+        }
+
         // Initialize font size variables.
         int fontSize = mainWebView.getSettings().getTextZoom();
         String fontSizeTitle;
@@ -2013,6 +2481,180 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         .show();
                 return true;
 
+            case R.id.easylist:
+                // Toggle the EasyList status.
+                easyListEnabled = !easyListEnabled;
+
+                // Update the menu checkbox.
+                menuItem.setChecked(easyListEnabled);
+
+                // Reload the main WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.easyprivacy:
+                // Toggle the EasyPrivacy status.
+                easyPrivacyEnabled = !easyPrivacyEnabled;
+
+                // Update the menu checkbox.
+                menuItem.setChecked(easyPrivacyEnabled);
+
+                // Reload the main WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.fanboys_annoyance_list:
+                // Toggle Fanboy's Annoyance List status.
+                fanboysAnnoyanceListEnabled = !fanboysAnnoyanceListEnabled;
+
+                // Update the menu checkbox.
+                menuItem.setChecked(fanboysAnnoyanceListEnabled);
+
+                // Update the staus of Fanboy's Social Blocking List.
+                MenuItem fanboysSocialBlockingListMenuItem = mainMenu.findItem(R.id.fanboys_social_blocking_list);
+                fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListEnabled);
+
+                // Reload the main WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.fanboys_social_blocking_list:
+                // Toggle Fanboy's Social Blocking List status.
+                fanboysSocialBlockingListEnabled = !fanboysSocialBlockingListEnabled;
+
+                // Update the menu checkbox.
+                menuItem.setChecked(fanboysSocialBlockingListEnabled);
+
+                // Reload the main WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.ultraprivacy:
+                // Toggle the UltraPrivacy status.
+                ultraPrivacyEnabled = !ultraPrivacyEnabled;
+
+                // Update the menu checkbox.
+                menuItem.setChecked(ultraPrivacyEnabled);
+
+                // Reload the main WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.block_all_third_party_requests:
+                //Toggle the third-party requests blocker status.
+                blockAllThirdPartyRequests = !blockAllThirdPartyRequests;
+
+                // Update the menu checkbox.
+                menuItem.setChecked(blockAllThirdPartyRequests);
+
+                // Reload the main WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.user_agent_privacy_browser:
+                // Update the user agent.
+                mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[0]);
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.user_agent_webview_default:
+                // Update the user agent.
+                mainWebView.getSettings().setUserAgentString("");
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.user_agent_firefox_on_android:
+                // Update the user agent.
+                mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[2]);
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.user_agent_chrome_on_android:
+                // Update the user agent.
+                mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[3]);
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.user_agent_safari_on_ios:
+                // Update the user agent.
+                mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[4]);
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.user_agent_firefox_on_linux:
+                // Update the user agent.
+                mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[5]);
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.user_agent_chromium_on_linux:
+                // Update the user agent.
+                mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[6]);
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.user_agent_firefox_on_windows:
+                // Update the user agent.
+                mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[7]);
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.user_agent_chrome_on_windows:
+                // Update the user agent.
+                mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[8]);
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.user_agent_edge_on_windows:
+                // Update the user agent.
+                mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[9]);
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.user_agent_internet_explorer_on_windows:
+                // Update the user agent.
+                mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[10]);
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.user_agent_safari_on_macos:
+                // Update the user agent.
+                mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[11]);
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.user_agent_custom:
+                // Update the user agent.
+                mainWebView.getSettings().setUserAgentString(defaultCustomUserAgentString);
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
             case R.id.font_size_twenty_five_percent:
                 mainWebView.getSettings().setTextZoom(25);
                 return true;
@@ -2062,6 +2704,35 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 onTheFlyDisplayImagesSet = true;
                 return true;
 
+            case R.id.night_mode:
+                // Toggle night mode.
+                nightMode = !nightMode;
+
+                // Enable or disable JavaScript according to night mode, the global preference, and any domain settings.
+                if (nightMode) {  // Night mode is enabled.  Enable JavaScript.
+                    // Update the global variable.
+                    javaScriptEnabled = true;
+                } else if (domainSettingsApplied) {  // Night mode is disabled and domain settings are applied.  Set JavaScript according to the domain settings.
+                    // Get the JavaScript preference that was stored the last time domain settings were loaded.
+                    javaScriptEnabled = domainSettingsJavaScriptEnabled;
+                } else {  // Night mode is disabled and domain settings are not applied.  Set JavaScript according to the global preference.
+                    // Get a handle for the shared preference.
+                    SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+                    // Get the JavaScript preference.
+                    javaScriptEnabled = sharedPreferences.getBoolean("javascript_enabled", false);
+                }
+
+                // Apply the JavaScript setting to the WebView.
+                mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
+
+                // Update the privacy icons.
+                updatePrivacyIcons(false);
+
+                // Reload the website.
+                mainWebView.reload();
+                return true;
+
             case R.id.view_source:
                 // Launch the View Source activity.
                 Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
@@ -2123,7 +2794,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 return true;
 
             case R.id.refresh:
-                mainWebView.reload();
+                if (menuItem.getTitle().equals(getString(R.string.refresh))) {  // The refresh button was pushed.
+                    // Reload the WebView.
+                    mainWebView.reload();
+                } else {  // The stop button was pushed.
+                    // Stop the loading of the WebView.
+                    mainWebView.stopLoading();
+                }
                 return true;
 
             case R.id.ad_consent:
@@ -2151,6 +2828,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case R.id.back:
                 if (mainWebView.canGoBack()) {
+                    // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+                    formattedUrlString = "";
+
                     // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
                     navigatingHistory = true;
 
@@ -2161,6 +2841,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case R.id.forward:
                 if (mainWebView.canGoForward()) {
+                    // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+                    formattedUrlString = "";
+
                     // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
                     navigatingHistory = true;
 
@@ -2178,6 +2861,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history));
                 break;
 
+            case R.id.requests:
+                // Launch the requests activity.
+                Intent requestsIntent = new Intent(this, RequestsActivity.class);
+                startActivity(requestsIntent);
+                break;
+
             case R.id.downloads:
                 // Launch the system Download Manager.
                 Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
@@ -2193,7 +2882,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 reapplyDomainSettingsOnRestart = true;
                 currentDomainName = "";
 
-                // Launch `DomainsActivity`.
+                // Launch the domains activity.
                 Intent domainsIntent = new Intent(this, DomainsActivity.class);
                 startActivity(domainsIntent);
                 break;
@@ -2206,7 +2895,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 reapplyDomainSettingsOnRestart = true;
                 currentDomainName = "";
 
-                // Launch `SettingsActivity`.
+                // Launch the settings activity.
                 Intent settingsIntent = new Intent(this, SettingsActivity.class);
                 startActivity(settingsIntent);
                 break;
@@ -2359,7 +3048,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
-        // Reload the ad for the free flavor if we are not in full screen mode.
+        // Reload the ad for the free flavor if we not in full screen mode.
         if (BuildConfig.FLAVOR.contentEquals("free") && !inFullScreenBrowsingMode) {
             // 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_id));
@@ -2988,6 +3677,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     @Override
     public void onSslMismatchBack() {
         if (mainWebView.canGoBack()) {  // There is a back page in the history.
+            // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+            formattedUrlString = "";
+
             // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
             navigatingHistory = true;
 
@@ -3007,6 +3699,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     @Override
     public void onUrlHistoryEntrySelected(int moveBackOrForwardSteps) {
+        // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+        formattedUrlString = "";
+
         // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
         navigatingHistory = true;
 
@@ -3039,6 +3734,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
 
         } else if (mainWebView.canGoBack()) {  // There is at least one item in the `WebView` history.
+            // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+            formattedUrlString = "";
+
             // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
             navigatingHistory = true;
 
@@ -3094,7 +3792,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             // Decode `formattedUri` as a `String` in `UTF-8`.
             formattedUrlString = URLDecoder.decode(formattedUri.build().toString(), "UTF-8");
-        } else {
+        } else if (unformattedUrlString.isEmpty()){  // Load a blank web site.
+            // Load a blank string.
+            formattedUrlString = "";
+        } else {  // Search for the contents of the URL box.
             // Sanitize the search input and convert it to a search.
             final String encodedUrlString = URLEncoder.encode(unformattedUrlString, "UTF-8");
 
@@ -3105,19 +3806,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Clear the focus from the URL text box.  Otherwise, proximate typing in the box will retain the colorized formatting instead of being reset during refocus.
         urlTextBox.clearFocus();
 
+        // Make it so.
         loadUrl(formattedUrlString);
     }
 
+    private void loadUrl(String url) {// Apply any custom domain settings.
+        // Set the URL as the formatted URL string so that checking third-party requests works correctly.
+        formattedUrlString = url;
 
-    private void loadUrl(String url) {
-        // Apply any custom domain settings.
+        // Apply the domain settings.
         applyDomainSettings(url, true, false);
 
+        // If loading a website, set `urlIsLoading` to prevent changes in the user agent on websites with redirects from reloading the current website.
+        urlIsLoading = !url.equals("");
+
         // Load the URL.
         mainWebView.loadUrl(url, customHeaders);
-
-        // Set `urlIsLoading` to prevent changes in the user agent on websites with redirects from reloading the current website.
-        urlIsLoading = true;
     }
 
     public void findPreviousOnPage(View view) {
@@ -3152,11 +3856,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
         // Store the values from the shared preferences in variables.
-        String homepageString = sharedPreferences.getString("homepage", "https://start.duckduckgo.com");
-        String torHomepageString = sharedPreferences.getString("tor_homepage", "https://3g2upl4pq6kufc4m.onion");
-        String torSearchString = sharedPreferences.getString("tor_search", "https://3g2upl4pq6kufc4m.onion/html/?q=");
+        String homepageString = sharedPreferences.getString("homepage", "https://searx.me/");
+        String torHomepageString = sharedPreferences.getString("tor_homepage", "http://ulrn6sryqaifefld.onion/");
+        String torSearchString = sharedPreferences.getString("tor_search", "http://ulrn6sryqaifefld.onion/?q=");
         String torSearchCustomURLString = sharedPreferences.getString("tor_search_custom_url", "");
-        String searchString = sharedPreferences.getString("search", "https://duckduckgo.com/html/?q=");
+        String searchString = sharedPreferences.getString("search", "https://searx.me/?q=");
         String searchCustomURLString = sharedPreferences.getString("search_custom_url", "");
         incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
         boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
@@ -3193,7 +3897,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.blue_50));
             }
 
-            // Display a message to the user if we are waiting on Orbot.
+            // Display a message to the user if waiting for Orbot.
             if (!orbotStatus.equals("ON")) {
                 // Set `waitingForOrbot`.
                 waitingForOrbot = true;
@@ -3239,7 +3943,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         }
 
         // Apply the appropriate full screen mode the `SYSTEM_UI` flags.
-        if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) {
+        if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) {  // Privacy Browser is currently in full screen browsing mode.
             if (hideSystemBarsOnFullscreen) {  // Hide everything.
                 // Remove the translucent navigation setting if it is currently flagged.
                 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
@@ -3256,6 +3960,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                  */
                 rootCoordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
             } else {  // Hide everything except the status and navigation bars.
+                // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`.
+                rootCoordinatorLayout.setSystemUiVisibility(0);
+
                 // Add the translucent status flag if it is unset.
                 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
 
@@ -3267,8 +3974,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
                 }
             }
-        } else {  // Switch to normal viewing mode.
-            // Reset `inFullScreenBrowsingMode` to `false`.
+        } 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 `appBar` if `findOnPageLinearLayout` is not visible.
@@ -3282,15 +3989,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getFragmentManager(), getString(R.string.ad_id));
             }
 
+            // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`.
+            rootCoordinatorLayout.setSystemUiVisibility(0);
+
             // Remove the translucent navigation bar flag if it is set.
             getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
 
             // Add the translucent status flag if it is unset.  This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`.
             getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
 
-            // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`.
-            rootCoordinatorLayout.setSystemUiVisibility(0);
-
             // Constrain `rootCoordinatorLayout` inside the status and navigation bars.
             rootCoordinatorLayout.setFitsSystemWindows(true);
         }
@@ -3300,9 +4007,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // The deprecated `.getDrawable()` must be used until the minimum API >= 21.
     @SuppressWarnings("deprecation")
     private void applyDomainSettings(String url, boolean resetFavoriteIcon, boolean reloadWebsite) {
-        // Reset `navigatingHistory`.
-        navigatingHistory = false;
-
         // Parse the URL into a URI.
         Uri uri = Uri.parse(url);
 
@@ -3321,6 +4025,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             loadingNewDomainName = !hostName.equals(currentDomainName);
         }
 
+        // Strings don't like to be null.
+        if (hostName == null) {
+            hostName = "";
+        }
+
         // Only apply the domain settings if a new domain is being loaded.  This allows the user to set temporary settings for JavaScript, cookies, DOM storage, etc.
         if (loadingNewDomainName) {
             // Set the new `hostname` as the `currentDomainName`.
@@ -3335,8 +4044,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 favoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(favoriteIconBitmap, 64, 64, true));
             }
 
-            // Initialize the database handler.  `this` specifies the context.  The two `nulls` do not specify the database name or a `CursorFactory`.
-            // The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
+            // 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);
 
             // Get a full cursor from `domainsDatabaseHelper`.
@@ -3370,26 +4078,28 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 domainNameInDatabase = hostName;
             }
 
-            // If `hostName` is not `null`, check all the subdomains of `hostName` against wildcard domains in `domainCursor`.
-            if (hostName != null) {
-                while (hostName.contains(".") && !domainSettingsApplied) {  // Stop checking if we run out of  `.` or if we already know that `domainSettingsApplied` is `true`.
-                    if (domainSettingsSet.contains("*." + hostName)) {  // Check the host name prepended by `*.`.
-                        domainSettingsApplied = true;
-                        domainNameInDatabase = "*." + hostName;
-                    }
+            // Check all the subdomains of the host name against wildcard domains in the domain cursor.
+            while (!domainSettingsApplied && hostName.contains(".")) {  // Stop checking if domain settings are already applied or there are no more `.` in the host name.
+                if (domainSettingsSet.contains("*." + hostName)) {  // Check the host name prepended by `*.`.
+                    // Apply the domain settings.
+                    domainSettingsApplied = true;
 
-                    // Strip out the lowest subdomain of `host`.
-                    hostName = hostName.substring(hostName.indexOf(".") + 1);
+                    // Store the applied domain names as it appears in the database.
+                    domainNameInDatabase = "*." + hostName;
                 }
+
+                // Strip out the lowest subdomain of of the host name.
+                hostName = hostName.substring(hostName.indexOf(".") + 1);
             }
 
-            // Get a handle for the shared preference.  `this` references the current context.
+
+            // Get a handle for the shared preference.
             SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
             // Store the general preference information.
             String defaultFontSizeString = sharedPreferences.getString("default_font_size", "100");
             String defaultUserAgentName = sharedPreferences.getString("user_agent", "Privacy Browser");
-            String defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0");
+            defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0");
             boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
             nightMode = sharedPreferences.getBoolean("night_mode", false);
 
@@ -3410,6 +4120,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 easyPrivacyEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1);
                 fanboysAnnoyanceListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1);
                 fanboysSocialBlockingListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1);
+                ultraPrivacyEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)) == 1);
+                blockAllThirdPartyRequests = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1);
                 String userAgentName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
                 int fontSize = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
                 int swipeToRefreshInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
@@ -3434,7 +4146,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         break;
                 }
 
-                // Set `javaScriptEnabled` to be `true` if `night_mode` is `true`.
+                // Store the domain JavaScript status.  This is used by the options menu night mode toggle.
+                domainSettingsJavaScriptEnabled = javaScriptEnabled;
+
+                // Enable JavaScript if night mode is enabled.
                 if (nightMode) {
                     javaScriptEnabled = true;
                 }
@@ -3554,7 +4269,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 } else {
                     urlAppBarRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
                 }
-            } else {  // The URL we are loading does not have custom domain settings.  Load the defaults.
+            } else {  // The new URL does not have custom domain settings.  Load the defaults.
                 // Store the values from `sharedPreferences` in variables.
                 javaScriptEnabled = sharedPreferences.getBoolean("javascript_enabled", false);
                 firstPartyCookiesEnabled = sharedPreferences.getBoolean("first_party_cookies_enabled", false);
@@ -3565,6 +4280,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 easyPrivacyEnabled = sharedPreferences.getBoolean("easyprivacy", true);
                 fanboysAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboy_annoyance_list", true);
                 fanboysSocialBlockingListEnabled = sharedPreferences.getBoolean("fanboy_social_blocking_list", true);
+                ultraPrivacyEnabled = sharedPreferences.getBoolean("ultraprivacy", true);
+                blockAllThirdPartyRequests = sharedPreferences.getBoolean("block_all_third_party_requests", false);
 
                 // Set `javaScriptEnabled` to be `true` if `night_mode` is `true`.
                 if (nightMode) {
@@ -3647,11 +4364,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             if (mainMenu != null) {
                 updatePrivacyIcons(true);
             }
+        }
 
-            // Reload the website if returning from the Domains activity.
-            if (reloadWebsite) {
-                mainWebView.reload();
-            }
+        // Reload the website if returning from the Domains activity.
+        if (reloadWebsite) {
+            mainWebView.reload();
         }
     }