]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
Release 2.11.
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / MainWebViewActivity.java
index e76e3e8fbcc5ce43780a5a2c49840128acb2e5b9..e7d7c66184203bdf289349e6ece38aba6650fcdc 100644 (file)
@@ -108,9 +108,9 @@ import android.widget.RadioButton;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
-import com.stoutner.privacybrowser.BannerAd;
 import com.stoutner.privacybrowser.BuildConfig;
 import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.dialogs.AdConsentDialog;
 import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog;
 import com.stoutner.privacybrowser.dialogs.CreateBookmarkFolderDialog;
 import com.stoutner.privacybrowser.dialogs.CreateHomeScreenShortcutDialog;
@@ -122,6 +122,7 @@ import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog;
 import com.stoutner.privacybrowser.dialogs.PinnedSslCertificateMismatchDialog;
 import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog;
 import com.stoutner.privacybrowser.dialogs.ViewSslCertificateDialog;
+import com.stoutner.privacybrowser.helpers.AdHelper;
 import com.stoutner.privacybrowser.helpers.BlockListHelper;
 import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
@@ -148,17 +149,17 @@ import java.util.Set;
 
 // AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21.
 public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
-        CreateHomeScreenShortcutDialog.CreateHomeScreenSchortcutListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener,
+        CreateHomeScreenShortcutDialog.CreateHomeScreenShortcutListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener,
         DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener, EditBookmarkFolderDialog.EditBookmarkFolderListener,
         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()`.
+    public static boolean allowScreenshots;
+
     // `favoriteIconBitmap` is public static so it can be accessed from `CreateHomeScreenShortcutDialog`, `BookmarksActivity`, `BookmarksDatabaseViewActivity`, `CreateBookmarkDialog`,
     // `CreateBookmarkFolderDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `EditBookmarkDatabaseViewDialog`, and `ViewSslCertificateDialog`.  It is also used in `onCreate()`,
     // `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onCreateHomeScreenShortcutCreate()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `applyDomainSettings()`.
@@ -196,6 +197,44 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     public static String fanboyAnnoyanceVersion;
     public static String fanboySocialVersion;
 
+    // The request items are public static so they can be accessed by `BlockListHelper`, `RequestsArrayAdapter`, and `ViewRequestsDialog`.  They are also used in `onCreate()`.
+    public static List<String[]> resourceRequests;
+    public static String[] whiteListResultStringArray;
+    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_BLOCKED = 2;
+
+    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;
+
     // `currentBookmarksFolder` is public static so it can be accessed from `BookmarksActivity`.  It is also used in `onCreate()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`,
     // `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
     public static String currentBookmarksFolder;
@@ -244,7 +283,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `fullScreenVideoFrameLayout` is used in `onCreate()` and `onConfigurationChanged()`.
     private FrameLayout fullScreenVideoFrameLayout;
 
-    // `swipeRefreshLayout` is used in `onCreate()`, `onPrepareOptionsMenu`, and `onRestart()`.
+    // `swipeRefreshLayout` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsMenuSelected()`, and `onRestart()`.
     private SwipeRefreshLayout swipeRefreshLayout;
 
     // `urlAppBarRelativeLayout` is used in `onCreate()` and `applyDomainSettings()`.
@@ -271,15 +310,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `domStorageEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.
     private boolean domStorageEnabled;
 
-    // `saveFormDataEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.
+    // `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()`.
     private boolean nightMode;
 
-    // `swipeToRefreshEnabled` is used in `onPrepareOptionsMenu()` and `applyAppSettings()`.
-    private boolean swipeToRefreshEnabled;
-
     // `displayWebpageImagesBoolean` is used in `applyAppSettings()` and `applyDomainSettings()`.
     private boolean displayWebpageImagesBoolean;
 
@@ -369,9 +405,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     private ForegroundColorSpan initialGrayColorSpan;
     private ForegroundColorSpan finalGrayColorSpan;
 
-    // `adView` is used in `onCreate()` and `onConfigurationChanged()`.
-    private View adView;
-
     // `sslErrorHandler` is used in `onCreate()`, `onSslErrorCancel()`, and `onSslErrorProceed`.
     private SslErrorHandler sslErrorHandler;
 
@@ -434,11 +467,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // Remove Android Studio's warning about deprecations.  We have to use the deprecated `getColor()` until API >= 23.
     @SuppressWarnings("deprecation")
     protected void onCreate(Bundle savedInstanceState) {
-        // Get a handle for `sharedPreferences`.  `this` references the current context.
+        // Get a handle for the shared preferences.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
-        // Get the theme preference.
+        // Get the theme and screenshot preferences.
         darkTheme = sharedPreferences.getBoolean("dark_theme", false);
+        allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
+
+        // Disable screenshots if not allowed.
+        if (!allowScreenshots) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
+        }
 
         // Set the activity theme.
         if (darkTheme) {
@@ -604,9 +643,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         // Hide the `appBar`.
                         appBar.hide();
 
-                        // Hide the `BannerAd` in the free flavor.
+                        // Hide the banner ad in the free flavor.
                         if (BuildConfig.FLAVOR.contentEquals("free")) {
-                            BannerAd.hideAd(adView);
+                            // The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+                            AdHelper.hideAd(findViewById(R.id.adview));
                         }
 
                         // Modify the system bars.
@@ -641,11 +681,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                         // Show the `BannerAd` in the free flavor.
                         if (BuildConfig.FLAVOR.contentEquals("free")) {
-                            // Reload the ad.  Because the screen may have rotated, we need to use `reloadAfterRotate`.
-                            BannerAd.reloadAfterRotate(adView, getApplicationContext(), getString(R.string.ad_id));
-
-                            // Reinitialize the `adView` variable, as the `View` will have been removed and re-added by `BannerAd.reloadAfterRotate()`.
-                            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_id));
                         }
 
                         // Remove the translucent navigation bar flag if it is set.
@@ -752,6 +789,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`.
@@ -814,7 +852,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             return true;
         });
 
-        // The `DrawerListener` allows us to update the Navigation Menu.
+        // The `DrawerListener` is used to update the Navigation Menu.
         drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
             @Override
             public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
@@ -831,10 +869,21 @@ 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.
+                    // Initialize a the blocked requests counter.
+                    int blockedRequests = 0;
+
+                    // Count the number of blocked requests.
+                    for (int i = 0; i < resourceRequests.size(); i++) {
+                        if (Integer.valueOf(resourceRequests.get(i)[REQUEST_DISPOSITION]) == REQUEST_BLOCKED) {
+                            blockedRequests++;
+                        }
+                    }
+
+                    // 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(getResources().getString(R.string.requests) + " - " + blockedRequests);
 
                     // Hide the keyboard (if displayed) so we can see the navigation menu.  `0` indicates no additional flags.
                     inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
@@ -898,7 +947,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         mainWebView.setVisibility(View.VISIBLE);
                     }
 
-                    //Stop the `SwipeToRefresh` indicator if it is running
+                    //Stop the swipe to refresh indicator if it is running
                     swipeRefreshLayout.setRefreshing(false);
                 }
             }
@@ -928,7 +977,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             public void onShowCustomView(View view, CustomViewCallback callback) {
                 // Pause the ad if this is the free flavor.
                 if (BuildConfig.FLAVOR.contentEquals("free")) {
-                    BannerAd.pauseAd(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));
                 }
 
                 // Remove the translucent overlays.
@@ -966,11 +1016,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Show the ad if this is the free flavor.
                 if (BuildConfig.FLAVOR.contentEquals("free")) {
-                    // Reload the ad.  Because the screen may have rotated, we need to use `reloadAfterRotate`.
-                    BannerAd.reloadAfterRotate(adView, getApplicationContext(), getString(R.string.ad_id));
-
-                    // Reinitialize the `adView` variable, as the `View` will have been removed and re-added by `BannerAd.reloadAfterRotate()`.
-                    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_id));
                 }
             }
 
@@ -1071,15 +1118,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Initialize `inFullScreenBrowsingMode`, which is always false at this point because Privacy Browser never starts in full screen browsing mode.
         inFullScreenBrowsingMode = false;
 
-        // Initialize AdView for the free flavor.
-        adView = findViewById(R.id.adview);
-
         // Initialize the privacy settings variables.
         javaScriptEnabled = false;
         firstPartyCookiesEnabled = false;
         thirdPartyCookiesEnabled = false;
         domStorageEnabled = false;
-        saveFormDataEnabled = false;
+        saveFormDataEnabled = false;  // Form data can be removed once the minimum API >= 26.
         nightMode = false;
 
         // Initialize the WebView title.
@@ -1106,6 +1150,9 @@ 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");
@@ -1125,10 +1172,10 @@ 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.
+                    // 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.
@@ -1185,13 +1232,16 @@ 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 `whiteListResultStringArray`.
+                whiteListResultStringArray = null;
+
                 // Check EasyList if it is enabled.
                 if (easyListEnabled) {
                     if (blockListHelper.isBlocked(formattedUrlString, url, easyList)) {
@@ -1221,6 +1271,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     }
                 }
 
+                // Add the request to the log.
+                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;
             }
@@ -1238,7 +1295,11 @@ 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();
+
+                // If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
                 if (nightMode) {
                     mainWebView.setVisibility(View.INVISIBLE);
                 }
@@ -1246,7 +1307,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;
@@ -1549,7 +1610,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         // Resume the adView for the free flavor.
         if (BuildConfig.FLAVOR.contentEquals("free")) {
-            BannerAd.resumeAd(adView);
+            // The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+            AdHelper.resumeAd(findViewById(R.id.adview));
         }
     }
 
@@ -1563,7 +1625,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         // Pause the adView or it will continue to consume resources in the background on the free flavor.
         if (BuildConfig.FLAVOR.contentEquals("free")) {
-            BannerAd.pauseAd(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));
         }
 
         super.onPause();
@@ -1584,23 +1647,33 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         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);
+        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 SDK >= 21
+        // Only display third-party cookies if API >= 21
         toggleThirdPartyCookiesMenuItem.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);
+
+        // 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.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
-        // Set the status of the additional app bar icons.  The default is `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)) {
             toggleFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
             toggleDomStorageMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
-            toggleSaveFormDataMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
         } else { //Do not display the additional icons.
             toggleFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
             toggleDomStorageMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-            toggleSaveFormDataMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+            refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
         }
 
         return true;
@@ -1613,14 +1686,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         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);
+        MenuItem toggleSaveFormDataMenuItem = 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);
+        MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data);  // Form data can be removed once the minimum API >= 26.
         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 refreshMenuItem = menu.findItem(R.id.refresh);
 
         // Set the text for the domain menu item.
         if (domainSettingsApplied) {
@@ -1633,7 +1706,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         toggleFirstPartyCookiesMenuItem.setChecked(firstPartyCookiesEnabled);
         toggleThirdPartyCookiesMenuItem.setChecked(thirdPartyCookiesEnabled);
         toggleDomStorageMenuItem.setChecked(domStorageEnabled);
-        toggleSaveFormDataMenuItem.setChecked(saveFormDataEnabled);
+        toggleSaveFormDataMenuItem.setChecked(saveFormDataEnabled);  // Form data can be removed once the minimum API >= 26.
+        swipeToRefreshMenuItem.setChecked(swipeRefreshLayout.isEnabled());
         displayImagesMenuItem.setChecked(mainWebView.getSettings().getLoadsImagesAutomatically());
 
         // Enable third-party cookies if first-party cookies are enabled.
@@ -1662,9 +1736,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Enable `Clear DOM Storage` if there is any.
         clearDOMStorageMenuItem.setEnabled(localStorageDirectoryNumberOfFiles > 0 || indexedDBDirectoryNumberOfFiles > 0);
 
-        // Enable `Clear Form Data` is there is any.
-        WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this);
-        clearFormDataMenuItem.setEnabled(mainWebViewDatabase.hasFormData());
+        // 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());
+        }
 
         // Enable `Clear Data` if any of the submenu items are enabled.
         clearDataMenuItem.setEnabled(clearCookiesMenuItem.isEnabled() || clearDOMStorageMenuItem.isEnabled() || clearFormDataMenuItem.isEnabled());
@@ -1726,9 +1802,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         fontSizeMenuItem.setTitle(fontSizeTitle);
         selectedFontSizeMenuItem.setChecked(true);
 
-        // Only show `Refresh` if `swipeToRefresh` is disabled.
-        refreshMenuItem.setVisible(!swipeToRefreshEnabled);
-
         // Run all the other default commands.
         super.onPrepareOptionsMenu(menu);
 
@@ -1747,6 +1820,29 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         // Set the commands that relate to the menu entries.
         switch (menuItemId) {
+            case R.id.toggle_javascript:
+                // Switch the status of javaScriptEnabled.
+                javaScriptEnabled = !javaScriptEnabled;
+
+                // Apply the new JavaScript status.
+                mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
+
+                // Update the privacy icon.  `true` runs `invalidateOptionsMenu` as the last step.
+                updatePrivacyIcons(true);
+
+                // Display a `Snackbar`.
+                if (javaScriptEnabled) {  // JavaScrip is enabled.
+                    Snackbar.make(findViewById(R.id.main_webview), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
+                } else if (firstPartyCookiesEnabled) {  // JavaScript is disabled, but first-party cookies are enabled.
+                    Snackbar.make(findViewById(R.id.main_webview), R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
+                } else {  // Privacy mode.
+                    Snackbar.make(findViewById(R.id.main_webview), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
+                }
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
             case R.id.add_or_edit_domain:
                 if (domainSettingsApplied) {  // Edit the current domain settings.
                     // Reapply the domain settings on returning to `MainWebViewActivity`.
@@ -1789,29 +1885,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 }
                 return true;
 
-            case R.id.toggle_javascript:
-                // Switch the status of javaScriptEnabled.
-                javaScriptEnabled = !javaScriptEnabled;
-
-                // Apply the new JavaScript status.
-                mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
-
-                // Update the privacy icon.  `true` runs `invalidateOptionsMenu` as the last step.
-                updatePrivacyIcons(true);
-
-                // Display a `Snackbar`.
-                if (javaScriptEnabled) {  // JavaScrip is enabled.
-                    Snackbar.make(findViewById(R.id.main_webview), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
-                } else if (firstPartyCookiesEnabled) {  // JavaScript is disabled, but first-party cookies are enabled.
-                    Snackbar.make(findViewById(R.id.main_webview), R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
-                } else {  // Privacy mode.
-                    Snackbar.make(findViewById(R.id.main_webview), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
-                }
-
-                // Reload the WebView.
-                mainWebView.reload();
-                return true;
-
             case R.id.toggle_first_party_cookies:
                 // Switch the status of firstPartyCookiesEnabled.
                 firstPartyCookiesEnabled = !firstPartyCookiesEnabled;
@@ -1828,7 +1901,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Display a `Snackbar`.
                 if (firstPartyCookiesEnabled) {  // First-party cookies are enabled.
                     Snackbar.make(findViewById(R.id.main_webview), R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
-                } else if (javaScriptEnabled){  // JavaScript is still enabled.
+                } else if (javaScriptEnabled) {  // JavaScript is still enabled.
                     Snackbar.make(findViewById(R.id.main_webview), R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
                 } else {  // Privacy mode.
                     Snackbar.make(findViewById(R.id.main_webview), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
@@ -1885,6 +1958,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 mainWebView.reload();
                 return true;
 
+            // Form data can be removed once the minimum API >= 26.
             case R.id.toggle_save_form_data:
                 // Switch the status of saveFormDataEnabled.
                 saveFormDataEnabled = !saveFormDataEnabled;
@@ -1977,6 +2051,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         .show();
                 return true;
 
+            // Form data can be remove once the minimum API >= 26.
             case R.id.clear_form_data:
                 Snackbar.make(findViewById(R.id.main_webview), R.string.form_data_deleted, Snackbar.LENGTH_LONG)
                         .setAction(R.string.undo, v -> {
@@ -2034,6 +2109,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 mainWebView.getSettings().setTextZoom(200);
                 return true;
 
+            case R.id.swipe_to_refresh:
+                // Toggle swipe to refresh.
+                swipeRefreshLayout.setEnabled(!swipeRefreshLayout.isEnabled());
+                return true;
+
             case R.id.display_images:
                 if (mainWebView.getSettings().getLoadsImagesAutomatically()) {  // Images are currently loaded automatically.
                     mainWebView.getSettings().setLoadsImagesAutomatically(false);
@@ -2046,6 +2126,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 onTheFlyDisplayImagesSet = true;
                 return true;
 
+            case R.id.view_source:
+                // Launch the View Source activity.
+                Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
+                startActivity(viewSourceIntent);
+                return true;
+
             case R.id.share:
                 // Setup the share string.
                 String shareString = webViewTitle + " â€“ " + urlTextBox.getText().toString();
@@ -2092,12 +2178,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
                 return true;
 
-            case R.id.view_source:
-                // Launch the Vew Source activity.
-                Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
-                startActivity(viewSourceIntent);
-                return true;
-
             case R.id.add_to_homescreen:
                 // Show the `CreateHomeScreenShortcutDialog` `AlertDialog` and name this instance `R.string.create_shortcut`.
                 AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog();
@@ -2110,6 +2190,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 mainWebView.reload();
                 return true;
 
+            case R.id.ad_consent:
+                // Display the ad consent dialog.
+                DialogFragment adConsentDialogFragment = new AdConsentDialog();
+                adConsentDialogFragment.show(getFragmentManager(), getString(R.string.ad_consent));
+                return true;
+
             default:
                 // Don't consume the event.
                 return super.onOptionsItemSelected(menuItem);
@@ -2156,6 +2242,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);
@@ -2171,7 +2263,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;
@@ -2184,7 +2276,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;
@@ -2247,8 +2339,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     }
                 }
 
-                // Clear form data.
-                if (clearEverything || sharedPreferences.getBoolean("clear_form_data", true)) {
+                // Clear form data if the API < 26.
+                if ((Build.VERSION.SDK_INT < 26) && (clearEverything || sharedPreferences.getBoolean("clear_form_data", true))) {
                     WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(this);
                     webViewDatabase.clearFormData();
 
@@ -2339,11 +2431,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         // Reload the ad for the free flavor if we are not in full screen mode.
         if (BuildConfig.FLAVOR.contentEquals("free") && !inFullScreenBrowsingMode) {
-            // Reload the ad.
-            BannerAd.reloadAfterRotate(adView, getApplicationContext(), getString(R.string.ad_id));
-
-            // Reinitialize the `adView` variable, as the `View` will have been removed and re-added by `BannerAd.reloadAfterRotate()`.
-            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_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:
@@ -3075,7 +3164,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");
 
@@ -3086,19 +3178,18 @@ 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.
+    private void loadUrl(String url) {// Apply any custom domain settings.
         applyDomainSettings(url, true, false);
 
-        // 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;
+
+        // Load the URL.
+        mainWebView.loadUrl(url, customHeaders);
     }
 
     public void findPreviousOnPage(View view) {
@@ -3145,7 +3236,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
         hideSystemBarsOnFullscreen = sharedPreferences.getBoolean("hide_system_bars", false);
         translucentNavigationBarOnFullscreen = sharedPreferences.getBoolean("translucent_navigation_bar", true);
-        swipeToRefreshEnabled = sharedPreferences.getBoolean("swipe_to_refresh", false);
         displayWebpageImagesBoolean = sharedPreferences.getBoolean("display_webpage_images", true);
 
         // Set the homepage, search, and proxy options.
@@ -3213,9 +3303,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             waitingForOrbot = false;
         }
 
-        // Set swipe to refresh.
-        swipeRefreshLayout.setEnabled(swipeToRefreshEnabled);
-
         // Set Do Not Track status.
         if (doNotTrackEnabled) {
             customHeaders.put("DNT", "1");
@@ -3263,11 +3350,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             // Show the `BannerAd` in the free flavor.
             if (BuildConfig.FLAVOR.contentEquals("free")) {
-                // Reload the ad.  Because the screen may have rotated, we need to use `reloadAfterRotate`.
-                BannerAd.reloadAfterRotate(adView, getApplicationContext(), getString(R.string.ad_id));
-
-                // Reinitialize the `adView` variable, as the `View` will have been removed and re-added by `BannerAd.reloadAfterRotate()`.
-                adView = findViewById(R.id.adview);
+                // 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 the translucent navigation bar flag if it is set.
@@ -3309,6 +3393,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`.
@@ -3358,26 +3447,29 @@ 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");
+            boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
             nightMode = sharedPreferences.getBoolean("night_mode", false);
 
             if (domainSettingsApplied) {  // The url we are loading has custom domain settings.
@@ -3391,6 +3483,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 firstPartyCookiesEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1);
                 thirdPartyCookiesEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1);
                 domStorageEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
+                // Form data can be removed once the minimum API >= 26.
                 saveFormDataEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1);
                 easyListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1);
                 easyPrivacyEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1);
@@ -3398,8 +3491,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 fanboysSocialBlockingListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1);
                 String userAgentName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
                 int fontSize = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
-                displayWebpageImagesInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
+                int swipeToRefreshInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
                 int nightModeInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE));
+                displayWebpageImagesInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
                 pinnedDomainSslCertificate = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1);
                 pinnedDomainSslIssuedToCNameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME));
                 pinnedDomainSslIssuedToONameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION));
@@ -3445,7 +3539,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
                 cookieManager.setAcceptCookie(firstPartyCookiesEnabled);
                 mainWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
-                mainWebView.getSettings().setSaveFormData(saveFormDataEnabled);
+
+                // Apply the form data setting if the API < 26.
+                if (Build.VERSION.SDK_INT < 26) {
+                    mainWebView.getSettings().setSaveFormData(saveFormDataEnabled);
+                }
 
                 // Apply the font size.
                 if (fontSize == 0) {  // Apply the default font size.
@@ -3508,6 +3606,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         }
                     }
 
+                    // Set swipe to refresh.
+                    switch (swipeToRefreshInt) {
+                        case DomainsDatabaseHelper.SWIPE_TO_REFRESH_SYSTEM_DEFAULT:
+                            // Set swipe to refresh according to the default.
+                            swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
+                            break;
+
+                        case DomainsDatabaseHelper.SWIPE_TO_REFRESH_ENABLED:
+                            // Enable swipe to refresh.
+                            swipeRefreshLayout.setEnabled(true);
+                            break;
+
+                        case DomainsDatabaseHelper.SWIPE_TO_REFRESH_DISABLED:
+                            // Disable swipe to refresh.
+                            swipeRefreshLayout.setEnabled(false);
+                    }
+
                     // Store the applied user agent string, which is used in the View Source activity.
                     appliedUserAgentString = mainWebView.getSettings().getUserAgentString();
                 }
@@ -3518,13 +3633,13 @@ 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);
                 thirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies_enabled", false);
                 domStorageEnabled = sharedPreferences.getBoolean("dom_storage_enabled", false);
-                saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data_enabled", false);
+                saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data_enabled", false);  // Form data can be removed once the minimum API >= 26.
                 easyListEnabled = sharedPreferences.getBoolean("easylist", true);
                 easyPrivacyEnabled = sharedPreferences.getBoolean("easyprivacy", true);
                 fanboysAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboy_annoyance_list", true);
@@ -3539,8 +3654,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
                 cookieManager.setAcceptCookie(firstPartyCookiesEnabled);
                 mainWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
-                mainWebView.getSettings().setSaveFormData(saveFormDataEnabled);
                 mainWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
+                swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
+
+                // Apply the form data setting if the API < 26.
+                if (Build.VERSION.SDK_INT < 26) {
+                    mainWebView.getSettings().setSaveFormData(saveFormDataEnabled);
+                }
 
                 // Reset the pinned SSL certificate information.
                 domainSettingsDatabaseId = -1;
@@ -3606,11 +3726,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();
         }
     }
 
@@ -3637,58 +3757,54 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     private void updatePrivacyIcons(boolean runInvalidateOptionsMenu) {
-        // Get handles for the icons.
-        MenuItem privacyIconMenuItem = mainMenu.findItem(R.id.toggle_javascript);
-        MenuItem firstPartyCookiesIconMenuItem = mainMenu.findItem(R.id.toggle_first_party_cookies);
-        MenuItem domStorageIconMenuItem = mainMenu.findItem(R.id.toggle_dom_storage);
-        MenuItem formDataIconMenuItem = mainMenu.findItem(R.id.toggle_save_form_data);
+        // Get handles for the menu items.
+        MenuItem privacyMenuItem = mainMenu.findItem(R.id.toggle_javascript);
+        MenuItem firstPartyCookiesMenuItem = mainMenu.findItem(R.id.toggle_first_party_cookies);
+        MenuItem domStorageMenuItem = mainMenu.findItem(R.id.toggle_dom_storage);
+        MenuItem refreshMenuItem = mainMenu.findItem(R.id.refresh);
 
-        // Update `privacyIcon`.
+        // Update the privacy icon.
         if (javaScriptEnabled) {  // JavaScript is enabled.
-            privacyIconMenuItem.setIcon(R.drawable.javascript_enabled);
+            privacyMenuItem.setIcon(R.drawable.javascript_enabled);
         } else if (firstPartyCookiesEnabled) {  // JavaScript is disabled but cookies are enabled.
-            privacyIconMenuItem.setIcon(R.drawable.warning);
+            privacyMenuItem.setIcon(R.drawable.warning);
         } else {  // All the dangerous features are disabled.
-            privacyIconMenuItem.setIcon(R.drawable.privacy_mode);
+            privacyMenuItem.setIcon(R.drawable.privacy_mode);
         }
 
-        // Update `firstPartyCookiesIcon`.
+        // Update the first-party cookies icon.
         if (firstPartyCookiesEnabled) {  // First-party cookies are enabled.
-            firstPartyCookiesIconMenuItem.setIcon(R.drawable.cookies_enabled);
+            firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled);
         } else {  // First-party cookies are disabled.
             if (darkTheme) {
-                firstPartyCookiesIconMenuItem.setIcon(R.drawable.cookies_disabled_dark);
+                firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_dark);
             } else {
-                firstPartyCookiesIconMenuItem.setIcon(R.drawable.cookies_disabled_light);
+                firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_light);
             }
         }
 
-        // Update `domStorageIcon`.
+        // Update the DOM storage icon.
         if (javaScriptEnabled && domStorageEnabled) {  // Both JavaScript and DOM storage are enabled.
-            domStorageIconMenuItem.setIcon(R.drawable.dom_storage_enabled);
+            domStorageMenuItem.setIcon(R.drawable.dom_storage_enabled);
         } else if (javaScriptEnabled) {  // JavaScript is enabled but DOM storage is disabled.
             if (darkTheme) {
-                domStorageIconMenuItem.setIcon(R.drawable.dom_storage_disabled_dark);
+                domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_dark);
             } else {
-                domStorageIconMenuItem.setIcon(R.drawable.dom_storage_disabled_light);
+                domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_light);
             }
         } else {  // JavaScript is disabled, so DOM storage is ghosted.
             if (darkTheme) {
-                domStorageIconMenuItem.setIcon(R.drawable.dom_storage_ghosted_dark);
+                domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_dark);
             } else {
-                domStorageIconMenuItem.setIcon(R.drawable.dom_storage_ghosted_light);
+                domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_light);
             }
         }
 
-        // Update `formDataIcon`.
-        if (saveFormDataEnabled) {  // Form data is enabled.
-            formDataIconMenuItem.setIcon(R.drawable.form_data_enabled);
-        } else {  // Form data is disabled.
-            if (darkTheme) {
-                formDataIconMenuItem.setIcon(R.drawable.form_data_disabled_dark);
-            } else {
-                formDataIconMenuItem.setIcon(R.drawable.form_data_disabled_light);
-            }
+        // Update the refresh icon.
+        if (darkTheme) {
+            refreshMenuItem.setIcon(R.drawable.refresh_enabled_dark);
+        } else {
+            refreshMenuItem.setIcon(R.drawable.refresh_enabled_light);
         }
 
         // `invalidateOptionsMenu` calls `onPrepareOptionsMenu()` and redraws the icons in the `AppBar`.