]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
Keep the favorite icon when returning from Settings or Domain Settings. Fixes https...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / MainWebViewActivity.java
index d330771672ccf83bd48954a1f8ca979dc0a2db48..c4715ec5a0fb4ae144dcf1c0c6a5f24e86956f32 100644 (file)
@@ -69,7 +69,6 @@ import android.text.Editable;
 import android.text.Spanned;
 import android.text.TextWatcher;
 import android.text.style.ForegroundColorSpan;
-import android.util.Log;
 import android.util.Patterns;
 import android.view.ContextMenu;
 import android.view.GestureDetector;
@@ -116,31 +115,29 @@ 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.BlockListHelper;
 import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
 import com.stoutner.privacybrowser.helpers.OrbotProxyHelper;
 import com.stoutner.privacybrowser.dialogs.DownloadFileDialog;
 import com.stoutner.privacybrowser.dialogs.SslCertificateErrorDialog;
 
-import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.io.UnsupportedEncodingException;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.regex.Pattern;
 
 // We need to use AppCompatActivity from android.support.v7.app.AppCompatActivity to have access to the SupportActionBar until the minimum API is >= 21.
 public class MainWebViewActivity extends AppCompatActivity implements AddDomainDialog.AddDomainListener, CreateBookmarkDialog.CreateBookmarkListener,
@@ -177,9 +174,6 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     // `appliedUserAgentString` is public static so it can be accessed from `ViewSourceActivity`.  It is also used in `applyDomainSettings()`.
     public static String appliedUserAgentString;
 
-    // `displayWebpageImagesBoolean` is public static so it can be accessed from `DomainSettingsFragment`.  It is also used in `applyAppSettings()` and `applyDomainSettings()`.
-    public static boolean displayWebpageImagesBoolean;
-
     // `reloadOnRestart` is public static so it can be accessed from `SettingsFragment`.  It is also used in `onRestart()`
     public static boolean reloadOnRestart;
 
@@ -189,8 +183,11 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     // `restartFromBookmarksActivity` is public static so it can be accessed from `BookmarksActivity`.  It is also used in `onRestart()`.
     public static boolean restartFromBookmarksActivity;
 
-    // `easyListVersion` is public static so it can be accessed from `AboutTabFragment`.  It is also used in `onCreate()`.
+    // 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;
 
     // `currentBookmarksFolder` is public static so it can be accessed from `BookmarksActivity`.  It is also used in `onCreate()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`,
     // `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
@@ -268,14 +265,20 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     // `swipeToRefreshEnabled` is used in `onPrepareOptionsMenu()` and `applyAppSettings()`.
     private boolean swipeToRefreshEnabled;
 
+    // `displayWebpageImagesBoolean` is used in `applyAppSettings()` and `applyDomainSettings()`.
+    private boolean displayWebpageImagesBoolean;
+
     // 'homepage' is used in `onCreate()`, `onNavigationItemSelected()`, and `applyAppSettings()`.
     private String homepage;
 
     // `searchURL` is used in `loadURLFromTextBox()` and `applyAppSettings()`.
     private String searchURL;
 
-    // `adBlockerEnabled` is used in `onCreate()` and `applyAppSettings()`.
-    private boolean adBlockerEnabled;
+    // The block list variables are used in `onCreate()` and `applyAppSettings()`.
+    private boolean easyListEnabled;
+    private boolean easyPrivacyEnabled;
+    private boolean fanboyAnnoyanceListEnabled;
+    private boolean fanboySocialBlockingListEnabled;
 
     // `privacyBrowserRuntime` is used in `onCreate()`, `onOptionsItemSelected()`, and `applyAppSettings()`.
     private Runtime privacyBrowserRuntime;
@@ -298,6 +301,9 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     // `reapplyDomainSettingsOnRestart` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, and `onAddDomain()`, .
     private boolean reapplyDomainSettingsOnRestart;
 
+    // `reapplyAppSettingsOnRestart` is used in `onNavigationItemSelected()` and `onRestart()`.
+    private boolean reapplyAppSettingsOnRestert;
+
     // `currentDomainName` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onAddDomain()`, and `applyDomainSettings()`.
     private String currentDomainName;
 
@@ -406,562 +412,6 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         // Run the default commands.
         super.onCreate(savedInstanceState);
 
-        Log.i("BlockLists", "Begin populating block lists.");
-
-        // Initialize the block lists.
-        List<String> mainWhiteList = new LinkedList<>();
-        List<String[]> multiEntryWhiteList = new LinkedList<>();
-        List<String[]> domainWhiteList = new LinkedList<>();
-        List<String[]> thirdPartyDomainWhiteList = new LinkedList<>();
-        List<String> mainBlockList = new LinkedList<>();
-        List<String> initialBlockList = new LinkedList<>();
-        List<String> finalBlockList = new LinkedList<>();
-        List<String> thirdPartyBlockList = new LinkedList<>();
-        List<String> thirdPartyInitialBlockList = new LinkedList<>();
-        List<String[]> multiEntryBlockList = new LinkedList<>();
-        List<String[]> multiEntryInitialBlockList = new LinkedList<>();
-        List<String[]> multiEntryFinalBlockList = new LinkedList<>();
-        List<String[]> domainBlockList = new LinkedList<>();
-        List<String[]> domainInitialBlockList = new LinkedList<>();
-        List<String[]> domainFinalBlockList = new LinkedList<>();
-        List<String[]> domainRegularExpressionBlockList = new LinkedList<>();
-        List<String[]> thirdPartyMultiEntryBlockList = new LinkedList<>();
-        List<String[]> thirdPartyMultiEntryInitialBlockList = new LinkedList<>();
-        List<String[]> thirdPartyDomainBlockList = new LinkedList<>();
-        List<String> regularExpressionBlockList = new LinkedList<>();
-        List<String> thirdPartyRegularExpressionBlockList = new LinkedList<>();
-
-        // Populate the block lists.
-        try {
-            // Load `easylist.txt` into a `BufferedReader`.
-            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(getAssets().open("easylist.txt")));
-
-            // Create a string for storing the block list entries.
-            String blockListEntry;
-
-            // Parse EasyList.
-            while ((blockListEntry = bufferedReader.readLine()) != null) {
-                // Remove any `^` from the block list entry.  Privacy Browser does not process them in the interest of efficiency.
-                blockListEntry = blockListEntry.replace("^", "");
-
-                //noinspection StatementWithEmptyBody
-                if (blockListEntry.contains("##") || blockListEntry.contains("#?#") || blockListEntry.contains("#@#") || blockListEntry.startsWith("[")) {
-                    // Entries that contain `##`, `#?#`, and `#@#` are for hiding elements in the main page's HTML.  Entries that start with `[` describe the AdBlock compatibility level.
-
-                    // Do nothing.  Privacy Browser does not currently use these entries.
-
-                    //Log.i("BlackLists", "Not added: " + adBlockerEntry);
-                } else if (blockListEntry.startsWith("!")){  //  Entries that begin with `!` are comments.
-                    if (blockListEntry.startsWith("! Version:")) {
-                        // Store the EasyList version number.
-                        easyListVersion = blockListEntry.substring(11);
-                    }
-
-                    //Log.i("BlackLists", "Not added: " + adBlockerEntry);
-                } else if (blockListEntry.startsWith("@@")) {  // Entries that begin with `@@` are whitelists.
-                    // Remove the `@@`
-                    blockListEntry = blockListEntry.substring(2);
-
-                    // Strip out an initial `||`  Privacy Browser doesn't differentiate against items that only match against the end of the domain name.
-                    if (blockListEntry.startsWith("||")) {
-                        blockListEntry = blockListEntry.substring(2);
-                    }
-
-                    // TODO
-
-                    // mainWhiteList.add(blockListEntry);
-
-                    // Log.i("BlockLists", "Main white list added: " + blockListEntry);
-                } else if (blockListEntry.endsWith("|")){  // Entries that end with `|` match against the end of the URL.
-                    // Strip out the final "|"
-                    blockListEntry = blockListEntry.substring(0, blockListEntry.length() - 1);
-
-                    // Strip out any initial `||`.  They are redundant in this case because the block list entry is being matched against the end of the URL.
-                    if (blockListEntry.startsWith("||")) {
-                        blockListEntry = blockListEntry.substring(2);
-                    }
-
-                    if (blockListEntry.contains("*")) {  // Process a double final entry.
-                        int wildcardIndex = blockListEntry.indexOf("*");
-
-                        String firstEntry = blockListEntry.substring(0, wildcardIndex);
-                        String secondEntry = blockListEntry.substring(wildcardIndex + 1);
-
-
-                        String[] doubleEntry = {firstEntry, secondEntry};
-
-                        multiEntryFinalBlockList.add(doubleEntry);
-
-                        //Log.i("BlockLists", "Multi entry final block list added: " + firstEntry + " , " + secondEntry).
-                    } else {  // Process a standard final entry.
-                        finalBlockList.add(blockListEntry);
-
-                        //Log.i("BlockLists", "Final block list added: " + blockListEntry);
-                    }
-                } else if (blockListEntry.contains("$")) {  // Entries that contain `$` use filter options.
-                    // Strip out any initial `||`.  These will be treated like any other entry.
-                    if (blockListEntry.startsWith("||")) {
-                        blockListEntry = blockListEntry.substring(2);
-                    }
-
-                    if (blockListEntry.contains("third-party")) {  // Process third party blocklist entries.
-                        //noinspection StatementWithEmptyBody
-                        if (blockListEntry.contains("~third-party")) {  // Process third-party white list entries.
-                            // Do not process these white list entries.  They are designed to combine with block filters that Privacy Browser doesn't use, like `subdocument` and `xmlhttprequest`.
-
-                            // Log.i("BlockLists", "Not added: " + blockListEntry);
-                        } else if (blockListEntry.contains("domain=")) {  // Process third-party domain block list entries.
-                            if (blockListEntry.startsWith("|")) {  // Third-party domain initial block list entries.
-
-                            } else if (blockListEntry.contains("\\")) {  // Third-party domain regular expressions.
-
-                            } else {  // Third-party domain entries.
-                                // Parse the entry
-                                String entry = blockListEntry.substring(0, blockListEntry.indexOf("$"));
-                                String filters = blockListEntry.substring(blockListEntry.indexOf("$") + 1);
-                                String domains = filters.substring(filters.indexOf("domain=") + 7);
-
-                                // Strip any trailing "*" on the entries.
-                                if (entry.endsWith("*")) {
-                                    entry = entry.substring(0, entry.length() - 1);
-                                }
-
-                                if (entry.contains("*")) {  // Third-party domain multi-entry.
-
-                                } else {  // Third-party domain single entry.
-
-                                    boolean whiteListDomain = false;
-
-                                    // Process each domain.
-                                    do {
-                                        String domain;
-
-                                        if (domains.contains("|")) {
-                                            // Get the first domain
-                                            domain = domains.substring(0, domains.indexOf("|"));
-
-                                            // Remove the first domain from the list.
-                                            domains = domains.substring(domains.indexOf("|") + 1);
-                                        } else {
-                                            domain = domains;
-                                        }
-
-                                        // Differentiate between block list domains and white list domains.
-                                        if (domain.startsWith("~")) {  // White list third-party domain entry.
-                                            // Strip the initial `~`.
-                                            domain = domain.substring(1);
-
-                                            // Set the white list domain flag and store the domain.
-                                            whiteListDomain = true;
-
-                                            // Create the domain entry.
-                                            String[] domainEntry = {domain, entry};
-
-                                            // Add the entry to the third-party domain white list.
-                                            thirdPartyDomainWhiteList.add(domainEntry);
-
-                                            Log.i("BlockLists", "Third-party domain white list added: " + domain + " , " + entry);
-                                        } else {  // Block list third-party domain entry.
-                                            String[] domainEntry = {domain, entry};
-
-                                            thirdPartyDomainBlockList.add(domainEntry);
-
-                                            // Log.i("BlockLists", "Third-party domain block list added: " + domain + " , " + entry);
-                                        }
-                                    } while (domains.contains("|"));
-
-                                    // Add a third-party block list entry if a white list domain was processed.
-                                    if (whiteListDomain) {
-                                        // Add an entry to the third-party block list.
-                                        thirdPartyBlockList.add(entry);
-
-                                        Log.i("BlockLists", "Third-party block list added: " + entry);
-                                    }
-                                }
-                            }
-                        } else if (blockListEntry.startsWith("|")) {  // Third-party initial block list entries.
-                            // Strip the initial `|`.
-                            blockListEntry = blockListEntry.substring(1);
-
-                            // Get the entry.
-                            String entry = blockListEntry.substring(0, blockListEntry.indexOf("$"));
-
-                            if (entry.contains("*")) {  // Process a third-party multi-entry initial block list.
-                                int wildcardIndex = entry.indexOf("*");
-
-                                String firstEntry = entry.substring(0, wildcardIndex);
-                                String secondEntry = entry.substring(wildcardIndex + 1);
-
-                                String[] thirdPartyDoubleEntry = {firstEntry, secondEntry};
-
-                                thirdPartyMultiEntryInitialBlockList.add(thirdPartyDoubleEntry);
-
-                                //Log.i("BlockLists", "Third-party multi-entry initial block list added: " + firstEntry + " , " + secondEntry);
-                            } else {
-                                thirdPartyInitialBlockList.add(entry);
-
-                                //Log.i("BlockLists", "Third-party initial block list added: " + entry);
-                            }
-                        } else if (blockListEntry.contains("*")) {  // Process third-party multi-entry or regular expression blocklist entries.
-                            // Get the entry.
-                            String entry = blockListEntry.substring(0, blockListEntry.indexOf("$"));
-
-                            if (entry.endsWith("*")) {
-                                // Strip the final `*`.
-                                entry = entry.substring(0, entry.length() - 1);
-
-                                // Add the entry to the block list.
-                                thirdPartyBlockList.add(entry);
-
-                                //Log.i("BlockLists", "Third party block list added: " + entry);
-                            } else if (entry.contains("\\")) {  // Process a third-party regular expression.
-                                // Add the entry to the third-party regular expression block list.
-                                thirdPartyRegularExpressionBlockList.add(entry);
-
-                                //Log.i("BlockLists", "Third-party regular expression block list added: " + entry);
-                            } else {  // There are two or more entries.
-                                int wildcardIndex = entry.indexOf("*");
-
-                                String firstEntry = entry.substring(0, wildcardIndex);
-                                String secondEntry = entry.substring(wildcardIndex + 1);
-
-                                if (secondEntry.contains("*")) {  // there are three or more entries.
-                                    int secondWildcardIndex = secondEntry.indexOf("*");
-
-                                    String realSecondEntry = secondEntry.substring(0, secondWildcardIndex);
-                                    String thirdEntry = secondEntry.substring(secondWildcardIndex + 1);
-
-                                    if (thirdEntry.contains("*")) {  // Process a third-party quadruple entry.
-                                        int thirdWildcardIndex = thirdEntry.indexOf("*");
-
-                                        String realThirdEntry = thirdEntry.substring(0, thirdWildcardIndex);
-                                        String fourthEntry = thirdEntry.substring(thirdWildcardIndex + 1);
-
-                                        String[] thirdPartyQuadrupleEntry = {firstEntry, realSecondEntry, realThirdEntry, fourthEntry};
-
-                                        thirdPartyMultiEntryBlockList.add(thirdPartyQuadrupleEntry);
-
-                                        //Log.i("BlockLists", "Third-party multi-entry block list added: " + firstEntry + " , " + realSecondEntry + " , " + realThirdEntry + " , " + fourthEntry);
-                                    } else {  // Process a third-party triple entry.
-                                        String[] thirdPartyTripleEntry = {firstEntry, realSecondEntry, thirdEntry};
-
-                                        thirdPartyMultiEntryBlockList.add(thirdPartyTripleEntry);
-
-                                        //Log.i("BlockLists", "Third-party multi-entry block list added: " + firstEntry + " , " + realSecondEntry + " , " + thirdEntry);
-                                    }
-                                } else {  // Process a third-party double entry.
-                                    String[] thirdPartyDoubleEntry = {firstEntry, secondEntry};
-
-                                    thirdPartyMultiEntryBlockList.add(thirdPartyDoubleEntry);
-
-                                    //Log.i("BlockLists", "Third-party multi-entry block list added: " + firstEntry + " , " + secondEntry);
-                                }
-                            }
-                        } else {  // Process standard third party entries.
-                            // Get the entry.
-                            String entry = blockListEntry.substring(0, blockListEntry.indexOf("$"));
-
-                            // Add the entry to the block list.
-                            thirdPartyBlockList.add(entry);
-
-                            //Log.i("BlockLists", "Third party block list added: " + entry);
-                        }
-                    } else if (blockListEntry.substring(blockListEntry.indexOf("$")).contains("domain")) {
-                        if (blockListEntry.contains("~")) {  // Whitelist.
-                            // Separate the filters.
-                            String entry = blockListEntry.substring(0, blockListEntry.indexOf("$"));
-                            String filters = blockListEntry.substring(blockListEntry.indexOf("$") + 1);
-                            String domains = filters.substring(filters.indexOf("domain=") + 7);
-
-                            // Strip any final `*` from the entry.  They are redundant.
-                            if (entry.endsWith("*")) {
-                                entry = entry.substring(0, entry.length() - 1);
-                            }
-
-                            // Process each domain.
-                            do {
-                                String domain;
-
-                                if (domains.contains("|")) {
-                                    // Get the first domain
-                                    domain = domains.substring(0, domains.indexOf("|"));
-
-                                    // Remove the first domain from the list.
-                                    domains = domains.substring(domains.indexOf("|") + 1);
-                                } else {
-                                    domain = domains;
-                                }
-
-                                // Strip the initial `~`.
-                                domain = domain.substring(1);
-
-                                if (entry.contains("*")) {  // Process a double entry.
-                                    int wildcardIndex = entry.indexOf("*");
-
-                                    String firstEntry = entry.substring(0, wildcardIndex);
-                                    String secondEntry = entry.substring(wildcardIndex + 1);
-
-                                    String[] domainDoubleEntry = {firstEntry, secondEntry};
-
-                                    domainWhiteList.add(domainDoubleEntry);
-
-                                    //Log.i("BlockLists", "Domain white list added: " + domain + " , " + firstEntry + " , " + secondEntry);
-                                } else {  // Process a single entry.
-                                    String[] domainEntry = {domain, entry};
-
-                                    domainWhiteList.add(domainEntry);
-
-                                    //Log.i("BlockLists", "Domain white list added: " + domain + " , " + entry);
-                                }
-                            } while (domains.contains("|"));
-                        } else {  // The block list entry contains a domain, but not a third party designation and isn't a whitelist.
-                            // Separate the filters.
-                            String entry = blockListEntry.substring(0, blockListEntry.indexOf("$"));
-                            String filters = blockListEntry.substring(blockListEntry.indexOf("$") + 1);
-                            String domains = filters.substring(filters.indexOf("domain=") + 7);
-
-                            // Only process the block list item if the entry is not null.  Some lines in EasyList begin with `$websocket`, which create a null entry.
-                            if (!entry.equals("")) {
-                                do {
-                                    String domain;
-
-                                    if (domains.contains("|")) {
-                                        // Get the first domain.
-                                        domain = domains.substring(0, domains.indexOf("|"));
-
-                                        // Remove the first domain from the list.
-                                        domains = domains.substring(domains.indexOf("|") + 1);
-                                    } else {
-                                        domain = domains;
-                                    }
-
-                                    if (entry.contains("*")) {
-                                        int wildcardIndex = entry.indexOf("*");
-
-                                        String firstEntry = entry.substring(0, wildcardIndex);
-                                        String secondEntry = entry.substring(wildcardIndex + 1);
-
-                                        String[] domainDoubleEntry = {domain, firstEntry, secondEntry};
-
-                                        domainBlockList.add(domainDoubleEntry);
-
-                                        //Log.i("BlockLists", "Domain double entry block list added: " + domain + " , " + firstEntry + " , " + secondEntry);
-                                    } else if (entry.startsWith("|")) {
-                                        // Remove the initial `|`;
-                                        String entryBase = entry.substring(1);
-
-                                        //noinspection StatementWithEmptyBody
-                                        if (entryBase.equals("http://") || entryBase.equals("https://")) {
-                                            // Do nothing.  These entries will entirely block the website.
-                                            // Often the original entry blocks `$script` but Privacy Browser does not currently differentiate between scripts and other entries.
-                                        } else {
-                                            String[] domainEntry = {domain, entryBase};
-
-                                            domainInitialBlockList.add(domainEntry);
-
-                                            //Log.i("BlockLists", "Domain initial block list added: " + domain + " , " + entryBase);
-                                        }
-                                    } else if (entry.endsWith("|")) {
-                                        // Remove the final `|`.
-                                        String entryBase = entry.substring(0, entry.length() - 1);
-
-                                        String[] domainEntry = {domain, entryBase};
-
-                                        domainFinalBlockList.add(domainEntry);
-
-                                        //Log.i("BlockLists", "Domain final block list added: " + domain + " , " + entryBase);
-                                    } else if (entry.contains("\\")) {
-                                        String[] domainEntry = {domain, entry};
-
-                                        domainRegularExpressionBlockList.add(domainEntry);
-
-                                        // Log.i("BlockLists", "Domain regular expression block list added: " + domain + " , " + entry);
-                                    } else {
-                                        String[] domainEntry = {domain, entry};
-
-                                        domainBlockList.add(domainEntry);
-
-                                        //Log.i("BlockLists", "Domain block list added: " + domain + " , " + entry);
-                                    }
-                                } while (domains.contains("|"));
-                            }
-                        }
-                    } else if (blockListEntry.contains("~")) {  // Whitelist entries.
-                        // Remove the filter options.
-                        blockListEntry = blockListEntry.substring(0, blockListEntry.indexOf("$"));
-
-                        // Strip any trailing `*`.
-                        if (blockListEntry.endsWith("*")) {
-                            blockListEntry = blockListEntry.substring(0, blockListEntry.length() -1);
-                        }
-
-                        if (blockListEntry.contains("*")) {
-                            int wildcardIndex = blockListEntry.indexOf("*");
-
-                            String firstEntry = blockListEntry.substring(0, wildcardIndex);
-                            String secondEntry = blockListEntry.substring(wildcardIndex + 1);
-
-
-                            String[] doubleEntry = {firstEntry, secondEntry};
-
-                            multiEntryWhiteList.add(doubleEntry);
-
-                            //Log.i("BlockLists", "Multi entry white list added: " + firstEntry + " , " + secondEntry);
-                        } else {
-                            mainWhiteList.add(blockListEntry);
-
-                            // Log.i("BlockLists", "Main white list added: " + blockListEntry);
-                        }
-                    } else if (blockListEntry.contains("\\")) {  // Regular expressions.
-                        // Remove the filter options.
-                        blockListEntry = blockListEntry.substring(0, blockListEntry.indexOf("$"));
-
-                        regularExpressionBlockList.add(blockListEntry);
-
-                        //Log.i("BlockLists", "Regular expression list added: " + blockListEntry);
-                    } else {
-                        // Remove the filter options.
-                        blockListEntry = blockListEntry.substring(0, blockListEntry.indexOf("$"));
-
-                        // Strip any trailing `*`.  These are redundant.
-                        if (blockListEntry.endsWith("*")) {
-                            blockListEntry = blockListEntry.substring(0, blockListEntry.length() - 1);
-                        }
-
-                        if (blockListEntry.contains("*")) {  // Use a multi entry list.
-                            int wildcardIndex = blockListEntry.indexOf("*");
-
-                            String firstEntry = blockListEntry.substring(0, wildcardIndex);
-                            String secondEntry = blockListEntry.substring(wildcardIndex + 1);
-
-                            // Remove `.*` if it appears at the beginning of the second entry.
-                            if (secondEntry.startsWith(".*")) {
-                                secondEntry = secondEntry.substring(2);
-                            }
-
-                            // Create a third entry if required.
-                            if (secondEntry.contains("*")) {
-                                wildcardIndex = secondEntry.indexOf("*");
-
-                                String thirdEntry = secondEntry.substring(wildcardIndex + 1);
-                                secondEntry = secondEntry.substring(0, wildcardIndex);
-
-
-                                if (thirdEntry.endsWith("|")) {
-                                    thirdEntry = thirdEntry.substring(0, thirdEntry.length() - 1);
-
-                                    String[] tripleEntry = {firstEntry, secondEntry, thirdEntry};
-
-                                    multiEntryFinalBlockList.add(tripleEntry);
-
-                                    //Log.i("BlockLists", "Multi entry final tripple block list added: " + firstEntry + " , " + secondEntry + " , " + thirdEntry);
-                                } else {
-                                    String[] tripleEntry = {firstEntry, secondEntry, thirdEntry};
-
-                                    multiEntryBlockList.add(tripleEntry);
-
-                                    // Log.i("BlockLists", "Multi entry tripple block list added: " + firstEntry + " , " + secondEntry + " , " + thirdEntry);
-                                }
-                            } else {  // This is a double entry.
-                                if (firstEntry.startsWith("|")) {
-                                    String[] doubleEntry = {firstEntry.substring(1), secondEntry};
-
-                                    multiEntryInitialBlockList.add(doubleEntry);
-
-                                    //Log.i("BlockLists", "Multi entry initial block list added: " + firstEntry.substring(1) + " , " + secondEntry);
-                                } else {
-                                    String[] doubleEntry = {firstEntry, secondEntry};
-
-                                    multiEntryBlockList.add(doubleEntry);
-
-                                    //Log.i("BlockLists", "Multi entry block list added: " + firstEntry + " , " + secondEntry);
-                                }
-                            }
-                        } else if (blockListEntry.startsWith("|")) {  // Populate the initial block list.
-                            // Strip the initial `|`.
-                            blockListEntry = blockListEntry.substring(1);
-
-                            // Populate the initial block list.
-                            initialBlockList.add(blockListEntry);
-
-                            //Log.i("BlockLists", "Initial block list added: " + blockListEntry);
-                        } else {  // Populate the main block list.
-                            mainBlockList.add(blockListEntry);
-
-                            //Log.i("BlockLists", "Main block list added: " + blockListEntry);
-                        }
-                    }
-                } else {  // Populate the standard lists.
-                    // Strip out any initial `||`.  These will be treated like any other entry.
-                    if (blockListEntry.startsWith("||")) {
-                        blockListEntry = blockListEntry.substring(2);
-                    }
-
-                    // Strip out any initial `*`.
-                    if (blockListEntry.startsWith("*")) {
-                        blockListEntry = blockListEntry.substring(1);
-                    }
-
-                    // Strip out any trailing `*`.
-                    if (blockListEntry.endsWith("*")) {
-                        blockListEntry = blockListEntry.substring(0, blockListEntry.length() - 1);
-                    }
-
-                    if (blockListEntry.contains("*")) {  // Entries that contain a `*` in the middle have to be treated specially.
-                        int wildcardIndex = blockListEntry.indexOf("*");
-
-                        String firstEntry = blockListEntry.substring(0, wildcardIndex);
-                        String secondEntry = blockListEntry.substring(wildcardIndex + 1);
-
-                        // Remove `.*` if it appears at the beginning of the second entry.
-                        if (secondEntry.startsWith(".*")) {
-                            secondEntry = secondEntry.substring(2);
-                        }
-
-                        // Create a third entry if required.
-                        if (secondEntry.contains("*")) {
-                            wildcardIndex = secondEntry.indexOf("*");
-
-                            String thirdEntry = secondEntry.substring(wildcardIndex + 1);
-                            secondEntry = secondEntry.substring(0, wildcardIndex);
-
-                            String[] tripleEntry = {firstEntry, secondEntry, thirdEntry};
-
-                            multiEntryBlockList.add(tripleEntry);
-
-                            //Log.i("BlockLists", "Multi entry tripple block list added: " + firstEntry + " , " + secondEntry + " , " + thirdEntry);
-                        } else {
-                            if (firstEntry.startsWith("|")) {
-                                String[] doubleEntry = {firstEntry.substring(1), secondEntry};
-
-                                multiEntryInitialBlockList.add(doubleEntry);
-
-                                //Log.i("BlockLists", "Multi entry initial block list added: " + firstEntry.substring(1) + " , " + secondEntry);
-                            } else {
-                                String[] doubleEntry = {firstEntry, secondEntry};
-
-                                multiEntryBlockList.add(doubleEntry);
-
-                                //Log.i("BlockLists", "Multi entry block list added: " + firstEntry + " , " + secondEntry);
-                            }
-                        }
-                    } else {  // This is a basic entry.
-                        // Add the modified block list entry to the main block list.
-                        mainBlockList.add(blockListEntry);
-
-                        //Log.i("BlockLists", "Main block list added: " + blockListEntry);
-                    }
-                }
-            }
-
-            // Close `bufferedReader`.
-            bufferedReader.close();
-        } catch (IOException e) {
-            // The asset exists, so the `IOException` will never be thrown.
-        }
-
-        Log.i("BlockLists", "Finish populating block lists");
-
         // Set the content view.
         setContentView(R.layout.main_drawerlayout);
 
@@ -1131,7 +581,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
 
                             /* 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 rehides them after they are shown.
+                             * 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);
 
@@ -1360,332 +810,313 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         // drawerToggle creates the hamburger icon at the start of the AppBar.
         drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, supportAppBar, R.string.open_navigation_drawer, R.string.close_navigation_drawer);
 
-        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.
-            // We have to use the deprecated `shouldOverrideUrlLoading` until API >= 24.
-            @SuppressWarnings("deprecation")
-            @Override
-            public boolean shouldOverrideUrlLoading(WebView view, String url) {
-                if (url.startsWith("mailto:")) {  // Load the email address in an external email program.
-                    // Use `ACTION_SENDTO` instead of `ACTION_SEND` so that only email programs are launched.
-                    Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
-
-                    // Parse the url and set it as the data for the `Intent`.
-                    emailIntent.setData(Uri.parse(url));
-
-                    // `FLAG_ACTIVITY_NEW_TASK` opens the email program in a new task instead as part of Privacy Browser.
-                    emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        // Get a handle for the progress bar.
+        final ProgressBar progressBar = findViewById(R.id.progress_bar);
 
-                    // Make it so.
-                    startActivity(emailIntent);
+        mainWebView.setWebChromeClient(new WebChromeClient() {
+            // Update the progress bar when a page is loading.
+            @Override
+            public void onProgressChanged(WebView view, int progress) {
+                // Inject the night mode CSS if night mode is enabled.
+                if (nightMode) {
+                    // `background-color: #212121` sets the background to be dark gray.  `color: #BDBDBD` sets the text color to be light gray.  `box-shadow: none` removes a lower underline on links
+                    // used by WordPress.  `text-decoration: none` removes all text underlines.  `text-shadow: none` removes text shadows, which usually have a hard coded color.
+                    // `border: none` removes all borders, which can also be used to underline text.
+                    // `a {color: #1565C0}` sets links to be a dark blue.  `!important` takes precedent over any existing sub-settings.
+                    mainWebView.evaluateJavascript("(function() {var parent = document.getElementsByTagName('head').item(0); var style = document.createElement('style'); style.type = 'text/css'; " +
+                            "style.innerHTML = '* {background-color: #212121 !important; color: #BDBDBD !important; box-shadow: none !important; text-decoration: none !important;" +
+                            "text-shadow: none !important; border: none !important;} a {color: #1565C0 !important;}'; parent.appendChild(style)})()", value -> {
+                                // Initialize a `Handler` to display `mainWebView`.
+                                Handler displayWebViewHandler = new Handler();
 
-                    // Returning `true` indicates the application is handling the URL.
-                    return true;
-                } else if (url.startsWith("tel:")) {  // Load the phone number in the dialer.
-                    // `ACTION_DIAL` open the dialer and loads the phone number, but waits for the user to place the call.
-                    Intent dialIntent = new Intent(Intent.ACTION_DIAL);
+                                // Setup a `Runnable` to display `mainWebView` after a delay to allow the CSS to be applied.
+                                Runnable displayWebViewRunnable = () -> {
+                                    // Only display `mainWebView` if the progress bar is one.  This prevents the display of the `WebView` while it is still loading.
+                                    if (progressBar.getVisibility() == View.GONE) {
+                                        mainWebView.setVisibility(View.VISIBLE);
+                                    }
+                                };
 
-                    // Add the phone number to the intent.
-                    dialIntent.setData(Uri.parse(url));
+                                // Use `displayWebViewHandler` to delay the displaying of `mainWebView` for 500 milliseconds.
+                                displayWebViewHandler.postDelayed(displayWebViewRunnable, 500);
+                            });
+                }
 
-                    // `FLAG_ACTIVITY_NEW_TASK` opens the dialer in a new task instead as part of Privacy Browser.
-                    dialIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                // Update the progress bar.
+                progressBar.setProgress(progress);
 
-                    // Make it so.
-                    startActivity(dialIntent);
+                // Set the visibility of the progress bar.
+                if (progress < 100) {
+                    // Show the progress bar.
+                    progressBar.setVisibility(View.VISIBLE);
+                } else {
+                    // Hide the progress bar.
+                    progressBar.setVisibility(View.GONE);
 
-                    // Returning `true` indicates the application is handling the URL.
-                    return true;
-                } else {  // Load the URL in Privacy Browser.
-                    // Apply the domain settings for the new URL.
-                    applyDomainSettings(url);
+                    // Display `mainWebView` if night mode is disabled.
+                    // Because of a race condition between `applyDomainSettings` and `onPageStarted`, when night mode is set by domain settings the `WebView` may be hidden even if night mode is not
+                    // currently enabled.
+                    if (!nightMode) {
+                        mainWebView.setVisibility(View.VISIBLE);
+                    }
 
-                    // Returning `false` causes the current `WebView` to handle the URL and prevents it from adding redirects to the history list.
-                    return false;
+                    //Stop the `SwipeToRefresh` indicator if it is running
+                    swipeRefreshLayout.setRefreshing(false);
                 }
             }
 
-            // Check requests against the block lists.  The deprecated `shouldInterceptRequest` must be used until minimum API >= 21.
-            @SuppressWarnings("deprecation")
+            // Set the favorite icon when it changes.
             @Override
-            public WebResourceResponse shouldInterceptRequest(WebView view, String url){
-                if (adBlockerEnabled) {  // Check the block lists.
-                    Log.i("BlockLists", "Begin check for " + url);
-
-                    Uri currentUri = Uri.parse(formattedUrlString);
-                    String currentDomain = currentUri.getHost();
-
-                    Uri requestUri = Uri.parse(url);
-                    String requestDomain = requestUri.getHost();
-
-                    boolean thirdPartyRequest = !requestDomain.equals(currentDomain);
+            public void onReceivedIcon(WebView view, Bitmap icon) {
+                // Only update the favorite icon if the website has finished loading.
+                if (progressBar.getVisibility() == View.GONE) {
+                    // Save a copy of the favorite icon.
+                    favoriteIconBitmap = icon;
 
-                    WebResourceResponse emptyWebResourceResponse = new WebResourceResponse("text/plain", "utf8", new ByteArrayInputStream("".getBytes()));
+                    // Place the favorite icon in the appBar.
+                    favoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(icon, 64, 64, true));
+                }
+            }
 
-                    for (String whiteListEntry : mainWhiteList) {
-                        if (url.contains(whiteListEntry)) {
-                            Log.i("BlockLists", "Request allowed by main white list: " + whiteListEntry + " | " + url);
+            // Save a copy of the title when it changes.
+            @Override
+            public void onReceivedTitle(WebView view, String title) {
+                // Save a copy of the title.
+                webViewTitle = title;
+            }
 
-                            // `Return null` loads the requested resource.
-                            return null;
-                        }
-                    }
+            // Enter full screen video
+            @Override
+            public void onShowCustomView(View view, CustomViewCallback callback) {
+                // Pause the ad if this is the free flavor.
+                if (BuildConfig.FLAVOR.contentEquals("free")) {
+                    BannerAd.pauseAd(adView);
+                }
 
-                    for (String[] whiteListEntry : multiEntryWhiteList) {
-                        if (whiteListEntry.length == 2) {  // There are two entries.
-                            if (url.contains(whiteListEntry[0]) && url.contains(whiteListEntry[1])) {
-                                Log.i("BlockLists", "Request allowed by multi entry white list:  " + whiteListEntry[0] + " , " + whiteListEntry[1] + " | " + url);
+                // Remove the translucent overlays.
+                getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
 
-                                // `Return null` loads the requested resource.
-                                return null;
-                            }
-                        } else {  // There are three entries.
-                            if (url.contains(whiteListEntry[0]) && url.contains(whiteListEntry[1]) && url.contains(whiteListEntry[2])) {
-                                Log.i("BlockLists", "Request allowed by multi entry white list:  " + whiteListEntry[0] + " , " + whiteListEntry[1] + " , " + whiteListEntry[2] + " | " + url);
+                // 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);
 
-                                // `Return null` loads the requested resource.
-                                return null;
-                            }
-                        }
-                    }
+                /* 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);
 
-                    for (String[] whiteListEntry : domainWhiteList) {
-                        if (requestDomain.endsWith(whiteListEntry[0]) && url.contains(whiteListEntry[1])) {
-                            Log.i("BlockLists", "Request allowed by domain white list: " + whiteListEntry[0] + " , " + whiteListEntry[1] + " | " + url);
+                // Set `rootCoordinatorLayout` to fill the entire screen.
+                rootCoordinatorLayout.setFitsSystemWindows(false);
 
-                            // `Return null` loads the requested resource.
-                            return null;
-                        }
-                    }
+                // Add `view` to `fullScreenVideoFrameLayout` and display it on the screen.
+                fullScreenVideoFrameLayout.addView(view);
+                fullScreenVideoFrameLayout.setVisibility(View.VISIBLE);
+            }
 
-                    // Only check the third-party white lists if this is a third-party request.
-                    if (thirdPartyRequest) {
-                        for (String[] whiteListEntry : thirdPartyDomainWhiteList) {
-                            if (requestDomain.endsWith(whiteListEntry[0]) && url.contains(whiteListEntry[1])) {
-                                Log.i("BlockLists", "Request allowed by third-party domain white list: " + whiteListEntry[0] + " , " + whiteListEntry[1] + " | " + url);
+            // Exit full screen video
+            public void onHideCustomView() {
+                // Hide `fullScreenVideoFrameLayout`.
+                fullScreenVideoFrameLayout.removeAllViews();
+                fullScreenVideoFrameLayout.setVisibility(View.GONE);
 
-                                // `Return null` loads the requested resource.
-                                return null;
-                            }
-                        }
-                    }
+                // Add the translucent status flag.  This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`.
+                getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
 
-                    for (String blockListEntry : mainBlockList) {
-                        if (url.contains(blockListEntry)) {
-                            Log.i("BlockLists", "Request blocked by main block list: " + blockListEntry + " | " + url);
+                // Set `rootCoordinatorLayout` to fit inside the status and navigation bars.  This also clears the `SYSTEM_UI` flags.
+                rootCoordinatorLayout.setFitsSystemWindows(true);
 
-                            // Return an empty `WebResourceResponse`.
-                            return emptyWebResourceResponse;
-                        }
-                    }
+                // 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));
 
-                    for (String blockListEntry : initialBlockList) {
-                        if (url.startsWith(blockListEntry)) {
-                            Log.i("BlockLists", "Request blocked by initial block list: " + blockListEntry + " | " + url);
+                    // Reinitialize the `adView` variable, as the `View` will have been removed and re-added by `BannerAd.reloadAfterRotate()`.
+                    adView = findViewById(R.id.adview);
+                }
+            }
+        });
 
-                            // Return an empty `WebResourceResponse`.
-                            return emptyWebResourceResponse;
-                        }
-                    }
+        // Register `mainWebView` for a context menu.  This is used to see link targets and download images.
+        registerForContextMenu(mainWebView);
 
-                    for (String blockListEntry : finalBlockList) {
-                        if (url.endsWith(blockListEntry)) {
-                            Log.i("BlockLists", "Request blocked by final block list: " + blockListEntry + " | " + url);
+        // Allow the downloading of files.
+        mainWebView.setDownloadListener((String url, String userAgent, String contentDisposition, String mimetype, long contentLength) -> {
+            // Show the `DownloadFileDialog` `AlertDialog` and name this instance `@string/download`.
+            AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength);
+            downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
+        });
 
-                            // Return an empty `WebResourceResponse`.
-                            return emptyWebResourceResponse;
-                        }
-                    }
+        // Allow pinch to zoom.
+        mainWebView.getSettings().setBuiltInZoomControls(true);
 
-                    for (String[] blockListEntry : multiEntryBlockList) {
-                        if (blockListEntry.length == 2) {  // There are two entries.
-                            if (url.contains(blockListEntry[0]) && url.contains(blockListEntry[1])) {
-                                Log.i("BlockLists", "Request blocked by multi entry block list: " + blockListEntry[0] + " , " + blockListEntry[1] + " | " + url);
+        // Hide zoom controls.
+        mainWebView.getSettings().setDisplayZoomControls(false);
 
-                                // Return an empty `WebResourceResponse`.
-                                return emptyWebResourceResponse;
-                            }
-                        } else {  // There are three entries.
-                            if (url.contains(blockListEntry[0]) && url.contains(blockListEntry[1]) && url.contains(blockListEntry[2])) {
-                                Log.i("BlockLists", "Request blocked by multi entry block list: " + blockListEntry[0] + " , " + blockListEntry[1] + " , " + blockListEntry[2] + " | " + url);
+        // Set `mainWebView` to use a wide viewport.  Otherwise, some web pages will be scrunched and some content will render outside the screen.
+        mainWebView.getSettings().setUseWideViewPort(true);
 
-                                // Return an empty `WebResourceResponse`.
-                                return emptyWebResourceResponse;
-                            }
-                        }
-                    }
+        // Set `mainWebView` to load in overview mode (zoomed out to the maximum width).
+        mainWebView.getSettings().setLoadWithOverviewMode(true);
 
-                    for (String[] blockListEntry : multiEntryInitialBlockList) {
-                        if (url.startsWith(blockListEntry[0]) && url.contains(blockListEntry[1])) {
-                            Log.i("BlockLists", "Request blocked by multi entry initial block list: " + blockListEntry[0] + " , " + blockListEntry[1] + " | " + url);
+        // Explicitly disable geolocation.
+        mainWebView.getSettings().setGeolocationEnabled(false);
 
-                            // Return an empty `WebResourceResponse`.
-                            return emptyWebResourceResponse;
-                        }
-                    }
+        // Initialize cookieManager.
+        cookieManager = CookieManager.getInstance();
 
-                    for (String[] blockListEntry : multiEntryFinalBlockList) {
-                        if (url.contains(blockListEntry[0]) && url.endsWith(blockListEntry[1])) {
-                            Log.i("BlockLists", "Request blocked by multi entry final block list: " + blockListEntry[0] + " , " + blockListEntry[1] + " | " + url);
+        // 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", "");
 
-                            // Return an empty `WebResourceResponse`.
-                            return emptyWebResourceResponse;
-                        }
-                    }
+        // 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.
+        PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
 
-                    for (String[] blockListEntry : domainBlockList) {
-                        if (blockListEntry.length == 2) {  // There is one entry.
-                            if (requestDomain.endsWith(blockListEntry[0]) && url.contains(blockListEntry[1])) {
-                                Log.i("BlockLists", "Request blocked by domain block list: " + blockListEntry[0] + " , " + blockListEntry[1] + " | " + url);
+        // Get the intent that started the app.
+        final Intent launchingIntent = getIntent();
 
-                                // Return an empty `WebResourceResponse`.
-                                return emptyWebResourceResponse;
-                            }
-                        } else { // There are two entries.
-                            if (requestDomain.endsWith(blockListEntry[0]) && url.contains(blockListEntry[1]) && url.contains(blockListEntry[2])) {
-                                Log.i("BlockLists", "Request blocked by domain block list: " + blockListEntry[0] + " , " + blockListEntry[1] + " , " + blockListEntry[2] + " | " + url);
+        // Extract the launching intent data as `launchingIntentUriData`.
+        final Uri launchingIntentUriData = launchingIntent.getData();
 
-                                // Return an empty `WebResourceResponse`.
-                                return emptyWebResourceResponse;
-                            }
-                        }
-                    }
+        // Convert the launching intent URI data (if it exists) to a string and store it in `formattedUrlString`.
+        if (launchingIntentUriData != null) {
+            formattedUrlString = launchingIntentUriData.toString();
+        }
 
-                    for (String[] blockListEntry : domainInitialBlockList) {
-                        if (requestDomain.endsWith(blockListEntry[0]) && url.startsWith(blockListEntry[1])) {
-                            Log.i("BlockLists", "Request blocked by domain initial block list: " + blockListEntry[0] + " , " + blockListEntry[1] + " | " + url);
+        // Get a handle for the `Runtime`.
+        privacyBrowserRuntime = Runtime.getRuntime();
 
-                            // Return an empty `WebResourceResponse`.
-                            return emptyWebResourceResponse;
-                        }
-                    }
+        // Store the application's private data directory.
+        privateDataDirectoryString = getApplicationInfo().dataDir;
+        // `dataDir` will vary, but will be something like `/data/user/0/com.stoutner.privacybrowser.standard`, which links to `/data/data/com.stoutner.privacybrowser.standard`.
 
-                    for (String[] blockListEntry : domainFinalBlockList) {
-                        if (requestDomain.endsWith(blockListEntry[0]) && url.endsWith(blockListEntry[2])) {
-                            Log.i("BlockLists", "Request blocked by domain final block list: " + blockListEntry[0] + " , " + blockListEntry[1] + " | " + url);
+        // Initialize `inFullScreenBrowsingMode`, which is always false at this point because Privacy Browser never starts in full screen browsing mode.
+        inFullScreenBrowsingMode = false;
 
-                            // Return an empty `WebResourceResponse`.
-                            return emptyWebResourceResponse;
-                        }
-                    }
+        // Initialize AdView for the free flavor.
+        adView = findViewById(R.id.adview);
 
-                    for (String[] blockListEntry : domainRegularExpressionBlockList) {
-                        if (requestDomain.endsWith(blockListEntry[0]) && Pattern.matches(blockListEntry[1], url)) {
-                            Log.i("BlockLists", "Request blocked by domain regular expression block list: " + blockListEntry[0] + " , " + blockListEntry[1] + " | " + url);
+        // Initialize the privacy settings variables.
+        javaScriptEnabled = false;
+        firstPartyCookiesEnabled = false;
+        thirdPartyCookiesEnabled = false;
+        domStorageEnabled = false;
+        saveFormDataEnabled = false;
+        nightMode = false;
 
-                            // Return an empty `WebResourceResponse`.
-                            return emptyWebResourceResponse;
-                        }
-                    }
+        // Initialize `webViewTitle`.
+        webViewTitle = getString(R.string.no_title);
 
-                    // Only check the third-party block lists if this is a third-party request.
-                    if (thirdPartyRequest) {
-                        for (String blockListEntry : thirdPartyBlockList) {
-                            if (url.contains(blockListEntry)) {
-                                Log.i("BlockLists", "Request blocked by third-party block list: " + blockListEntry + " | " + url);
+        // Initialize `favoriteIconBitmap`.  `ContextCompat` must be used until API >= 21.
+        Drawable favoriteIconDrawable = ContextCompat.getDrawable(getApplicationContext(), R.drawable.world);
+        BitmapDrawable favoriteIconBitmapDrawable = (BitmapDrawable) favoriteIconDrawable;
+        assert favoriteIconBitmapDrawable != null;
+        favoriteIconDefaultBitmap = favoriteIconBitmapDrawable.getBitmap();
 
-                                // Return an empty `WebResourceResponse`.
-                                return emptyWebResourceResponse;
-                            }
-                        }
+        // If the favorite icon is null, load the default.
+        if (favoriteIconBitmap == null) {
+            favoriteIconBitmap = favoriteIconDefaultBitmap;
+        }
 
-                        for (String blockListEntry : thirdPartyInitialBlockList) {
-                            if (url.startsWith(blockListEntry)) {
-                                Log.i("BlockLists", "Request blocked by third-party initial block list: " + blockListEntry + " | " + url);
+        // Apply the app settings from the shared preferences.
+        applyAppSettings();
 
-                                // Return an empty `WebResourceResponse`.
-                                return emptyWebResourceResponse;
-                            }
-                        }
+        // Instantiate the block list helper.
+        BlockListHelper blockListHelper = new BlockListHelper();
 
-                        for (String[] blockListEntry : thirdPartyMultiEntryBlockList) {
-                            switch (blockListEntry.length) {
-                                case 2:  // There are two entries.
-                                    if (url.contains(blockListEntry[0]) && url.contains(blockListEntry[1])) {
-                                        Log.i("BlockLists", "Request blocked by third-party multi-entry block list: " + blockListEntry[0] + " , " + blockListEntry[1] + " | " + url);
+        // 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");
 
-                                        // Return an empty `WebResourceResponse`.
-                                        return emptyWebResourceResponse;
-                                    }
-                                    break;
+        // 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];
 
-                                case 3:  // There are three entries.
-                                    if (url.contains(blockListEntry[0]) && url.contains(blockListEntry[1]) && url.contains(blockListEntry[2])) {
-                                        Log.i("BlockLists", "Request blocked by third-party multi-entry block list: " + blockListEntry[0] + " , " + blockListEntry[1] + " , " + blockListEntry[2] +
-                                                " | " + url);
+        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.
+            // We have to use the deprecated `shouldOverrideUrlLoading` until API >= 24.
+            @SuppressWarnings("deprecation")
+            @Override
+            public boolean shouldOverrideUrlLoading(WebView view, String url) {
+                if (url.startsWith("mailto:")) {  // Load the email address in an external email program.
+                    // Use `ACTION_SENDTO` instead of `ACTION_SEND` so that only email programs are launched.
+                    Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
 
-                                        // Return an empty `WebResourceResponse`.
-                                        return emptyWebResourceResponse;
-                                    }
-                                    break;
+                    // Parse the url and set it as the data for the `Intent`.
+                    emailIntent.setData(Uri.parse(url));
 
-                                case 4:  // There are four entries.
-                                    if (url.contains(blockListEntry[0]) && url.contains(blockListEntry[1]) && url.contains(blockListEntry[2]) && url.contains(blockListEntry[3])) {
-                                        Log.i("BlockLists", "Request blocked by third-party multi-entry block list: " + blockListEntry[0] + " , " + blockListEntry[1] + " , " + blockListEntry[2] +
-                                                " , " + blockListEntry[3] + " | " + url);
+                    // `FLAG_ACTIVITY_NEW_TASK` opens the email program in a new task instead as part of Privacy Browser.
+                    emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-                                        // Return an empty `WebResourceResponse`.
-                                        return emptyWebResourceResponse;
-                                    }
-                                    break;
-                            }
-                        }
+                    // Make it so.
+                    startActivity(emailIntent);
 
-                        for (String[] blockListEntry : thirdPartyMultiEntryInitialBlockList) {
-                            if (url.startsWith(blockListEntry[0]) && url.contains(blockListEntry[1])) {
-                                Log.i("BlockLists", "Request blocked by third-party multi-entry initial block list");
+                    // Returning `true` indicates the application is handling the URL.
+                    return true;
+                } else if (url.startsWith("tel:")) {  // Load the phone number in the dialer.
+                    // `ACTION_DIAL` open the dialer and loads the phone number, but waits for the user to place the call.
+                    Intent dialIntent = new Intent(Intent.ACTION_DIAL);
 
-                                // Return an empty `WebResourceResponse`.
-                                return emptyWebResourceResponse;
-                            }
-                        }
+                    // Add the phone number to the intent.
+                    dialIntent.setData(Uri.parse(url));
 
-                        for (String[] blockListEntry : thirdPartyDomainBlockList) {
-                            if (blockListEntry.length == 2) {  // There is one entry.
-                                if (requestDomain.endsWith(blockListEntry[0]) && url.contains(blockListEntry[1])) {
-                                    Log.i("BlockLists", "Request blocked by third-party domain block list: " + blockListEntry[0] + " , " + blockListEntry[1] + " | " + url);
+                    // `FLAG_ACTIVITY_NEW_TASK` opens the dialer in a new task instead as part of Privacy Browser.
+                    dialIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-                                    // Return an empty `WebResourceResponse`.
-                                    return emptyWebResourceResponse;
-                                }
-                            } else { // There are two entries.
-                                if (requestDomain.endsWith(blockListEntry[0]) && url.contains(blockListEntry[1]) && url.contains(blockListEntry[2])) {
-                                    Log.i("BlockLists", "Request blocked by third-party domain block list: " + blockListEntry[0] + " , " + blockListEntry[1] +  " , " + blockListEntry[2] + " | " +
-                                            url);
+                    // Make it so.
+                    startActivity(dialIntent);
 
-                                    // Return an empty `WebResourceResponse`.
-                                    return emptyWebResourceResponse;
-                                }
-                            }
-                        }
+                    // Returning `true` indicates the application is handling the URL.
+                    return true;
+                } else {  // Load the URL in Privacy Browser.
+                    // Apply the domain settings for the new URL.
+                    applyDomainSettings(url, true);
 
-                        for (String blockListEntry : thirdPartyRegularExpressionBlockList) {
-                            if (Pattern.matches(blockListEntry, url)) {
-                                Log.i("BlockLists", "Request blocked by third-party regular expression block list: " + blockListEntry + " | " + url);
+                    // Returning `false` causes the current `WebView` to handle the URL and prevents it from adding redirects to the history list.
+                    return false;
+                }
+            }
 
-                                // Return an empty `WebResourceResponse`.
-                                return emptyWebResourceResponse;
-                            }
-                        }
+            // 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()));
+
+                // Check EasyList if it is enabled.
+                if (easyListEnabled) {
+                    if (blockListHelper.isBlocked(formattedUrlString, url, easyList)) {
+                        // The resource request was blocked.  Return an empty web resource response.
+                        return emptyWebResourceResponse;
                     }
+                }
 
-                    for (String blockListEntry : regularExpressionBlockList) {
-                        if (Pattern.matches(blockListEntry, url)) {
-                            Log.i("BlockLists", "Request blocked by regular expression block list: " + blockListEntry + " | " + url);
-
-                            // Return an empty `WebResourceResponse`.
-                            return emptyWebResourceResponse;
-                        }
+                // Check EasyPrivacy if it is enabled.
+                if (easyPrivacyEnabled) {
+                    if (blockListHelper.isBlocked(formattedUrlString, url, easyPrivacy)) {
+                        // The resource request was blocked.  Return an empty web resource response.
+                        return emptyWebResourceResponse;
                     }
+                }
 
-                    Log.i("BlockLists", "End check for " + url);
-
-                    // `return null` loads the requested resource.
-                    return null;
-                } else {  // Ad blocking is disabled.
-                    // `return null` loads the requested resource.
-                    return null;
+                // Check Fanboy’s Annoyance List if it is enabled.
+                if (fanboyAnnoyanceListEnabled) {
+                    if (blockListHelper.isBlocked(formattedUrlString, url, fanboyAnnoyance)) {
+                        // The resource request was blocked.  Return an empty web resource response.
+                        return emptyWebResourceResponse;
+                    }
+                } else if (fanboySocialBlockingListEnabled){  // Only check Fanboy’s Social Blocking List if Fanboy’s Annoyance List is disabled.
+                    if (blockListHelper.isBlocked(formattedUrlString, url, fanboySocial)) {
+                        // The resource request was blocked.  Return an empty web resource response.
+                        return emptyWebResourceResponse;
+                    }
                 }
+
+                // The resource request has not been blocked.  `return null` loads the requested resource.
+                return null;
             }
 
             // Handle HTTP authentication requests.
@@ -1701,8 +1132,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
 
             // 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) {// If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
                 if (nightMode) {
                     mainWebView.setVisibility(View.INVISIBLE);
                 }
@@ -1723,7 +1153,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
 
                     // Apply any custom domain settings if the URL was loaded by navigating history.
                     if (navigatingHistory) {
-                        applyDomainSettings(url);
+                        applyDomainSettings(url, true);
                     }
 
                     // 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.
@@ -1734,6 +1164,11 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
             // It is necessary to update `formattedUrlString` and `urlTextBox` after the page finishes loading because the final URL can change during load.
             @Override
             public void onPageFinished(WebView view, String url) {
+                // Flush any cookies to persistent storage.  `CookieManager` has become very lazy about flushing cookies in recent versions.
+                if (firstPartyCookiesEnabled && Build.VERSION.SDK_INT >= 21) {
+                    cookieManager.flush();
+                }
+
                 // Reset `urlIsLoading`, which is used to prevent reloads on redirect if the user agent changes.
                 urlIsLoading = false;
 
@@ -1773,7 +1208,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                         inputMethodManager.showSoftInput(urlTextBox, 0);
 
                         // Apply the domain settings.  This clears any settings from the previous domain.
-                        applyDomainSettings(formattedUrlString);
+                        applyDomainSettings(formattedUrlString, true);
                     } else {  // `WebView` has loaded a webpage.
                         // Set `formattedUrlString`.
                         formattedUrlString = url;
@@ -1844,7 +1279,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                                 !currentWebsiteIssuedToUName.equals(pinnedDomainSslIssuedToUNameString) || !currentWebsiteIssuedByCName.equals(pinnedDomainSslIssuedByCNameString) ||
                                 !currentWebsiteIssuedByOName.equals(pinnedDomainSslIssuedByONameString) || !currentWebsiteIssuedByUName.equals(pinnedDomainSslIssuedByUNameString) ||
                                 !currentWebsiteSslStartDateString.equals(pinnedDomainSslStartDateString) || !currentWebsiteSslEndDateString.equals(pinnedDomainSslEndDateString)) {
-                                // The pinned SSL certificate doesn't match the current domain certificate.
+                            // The pinned SSL certificate doesn't match the current domain certificate.
                             //Display the pinned SSL certificate mismatch `AlertDialog`.
                             AppCompatDialogFragment pinnedSslCertificateMismatchDialogFragment = new PinnedSslCertificateMismatchDialog();
                             pinnedSslCertificateMismatchDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_mismatch));
@@ -1875,7 +1310,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                         currentWebsiteIssuedToUName.equals(pinnedDomainSslIssuedToUNameString) && currentWebsiteIssuedByCName.equals(pinnedDomainSslIssuedByCNameString) &&
                         currentWebsiteIssuedByOName.equals(pinnedDomainSslIssuedByONameString) && currentWebsiteIssuedByUName.equals(pinnedDomainSslIssuedByUNameString) &&
                         currentWebsiteSslStartDate.equals(pinnedDomainSslStartDate) && currentWebsiteSslEndDate.equals(pinnedDomainSslEndDate)) {
-                        // An SSL certificate is pinned and matches the current domain certificate.
+                    // An SSL certificate is pinned and matches the current domain certificate.
                     // Proceed to the website without displaying an error.
                     handler.proceed();
                 } else {  // Either there isn't a pinned SSL certificate or it doesn't match the current website certificate.
@@ -1889,216 +1324,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
             }
         });
 
-        // Get a handle for the progress bar.
-        final ProgressBar progressBar = findViewById(R.id.progress_bar);
-
-        mainWebView.setWebChromeClient(new WebChromeClient() {
-            // Update the progress bar when a page is loading.
-            @Override
-            public void onProgressChanged(WebView view, int progress) {
-                // Inject the night mode CSS if night mode is enabled.
-                if (nightMode) {
-                    // `background-color: #212121` sets the background to be dark gray.  `color: #BDBDBD` sets the text color to be light gray.  `box-shadow: none` removes a lower underline on links
-                    // used by WordPress.  `text-decoration: none` removes all text underlines.  `text-shadow: none` removes text shadows, which usually have a hard coded color.
-                    // `border: none` removes all borders, which can also be used to underline text.
-                    // `a {color: #1565C0}` sets links to be a dark blue.  `!important` takes precedent over any existing sub-settings.
-                    mainWebView.evaluateJavascript("(function() {var parent = document.getElementsByTagName('head').item(0); var style = document.createElement('style'); style.type = 'text/css'; " +
-                            "style.innerHTML = '* {background-color: #212121 !important; color: #BDBDBD !important; box-shadow: none !important; text-decoration: none !important;" +
-                            "text-shadow: none !important; border: none !important;} a {color: #1565C0 !important;}'; parent.appendChild(style)})()", value -> {
-                                // Initialize a `Handler` to display `mainWebView`.
-                                Handler displayWebViewHandler = new Handler();
-
-                                // Setup a `Runnable` to display `mainWebView` after a delay to allow the CSS to be applied.
-                                Runnable displayWebViewRunnable = () -> {
-                                    // Only display `mainWebView` if the progress bar is one.  This prevents the display of the `WebView` while it is still loading.
-                                    if (progressBar.getVisibility() == View.GONE) {
-                                        mainWebView.setVisibility(View.VISIBLE);
-                                    }
-                                };
-
-                                // Use `displayWebViewHandler` to delay the displaying of `mainWebView` for 500 milliseconds.
-                                displayWebViewHandler.postDelayed(displayWebViewRunnable, 500);
-                            });
-                }
-
-                // Update the progress bar.
-                progressBar.setProgress(progress);
-
-                // Set the visibility of the progress bar.
-                if (progress < 100) {
-                    // Show the progress bar.
-                    progressBar.setVisibility(View.VISIBLE);
-                } else {
-                    // Hide the progress bar.
-                    progressBar.setVisibility(View.GONE);
-
-                    // Display `mainWebView` if night mode is disabled.
-                    // Because of a race condition between `applyDomainSettings` and `onPageStarted`, when night mode is set by domain settings the `WebView` may be hidden even if night mode is not
-                    // currently enabled.
-                    if (!nightMode) {
-                        mainWebView.setVisibility(View.VISIBLE);
-                    }
-
-                    //Stop the `SwipeToRefresh` indicator if it is running
-                    swipeRefreshLayout.setRefreshing(false);
-                }
-            }
-
-            // Set the favorite icon when it changes.
-            @Override
-            public void onReceivedIcon(WebView view, Bitmap icon) {
-                // Only update the favorite icon if the website has finished loading.
-                if (progressBar.getVisibility() == View.GONE) {
-                    // Save a copy of the favorite icon.
-                    favoriteIconBitmap = icon;
-
-                    // Place the favorite icon in the appBar.
-                    favoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(icon, 64, 64, true));
-                }
-            }
-
-            // Save a copy of the title when it changes.
-            @Override
-            public void onReceivedTitle(WebView view, String title) {
-                // Save a copy of the title.
-                webViewTitle = title;
-            }
-
-            // Enter full screen video
-            @Override
-            public void onShowCustomView(View view, CustomViewCallback callback) {
-                // Pause the ad if this is the free flavor.
-                if (BuildConfig.FLAVOR.contentEquals("free")) {
-                    BannerAd.pauseAd(adView);
-                }
-
-                // 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 rehides them after they are shown.
-                 */
-                rootCoordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
-
-                // Set `rootCoordinatorLayout` to fill the entire screen.
-                rootCoordinatorLayout.setFitsSystemWindows(false);
-
-                // Add `view` to `fullScreenVideoFrameLayout` and display it on the screen.
-                fullScreenVideoFrameLayout.addView(view);
-                fullScreenVideoFrameLayout.setVisibility(View.VISIBLE);
-            }
-
-            // Exit full screen video
-            public void onHideCustomView() {
-                // 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);
-
-                // Set `rootCoordinatorLayout` to fit inside the status and navigation bars.  This also clears the `SYSTEM_UI` flags.
-                rootCoordinatorLayout.setFitsSystemWindows(true);
-
-                // 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);
-                }
-            }
-        });
-
-        // Register `mainWebView` for a context menu.  This is used to see link targets and download images.
-        registerForContextMenu(mainWebView);
-
-        // Allow the downloading of files.
-        mainWebView.setDownloadListener((String url, String userAgent, String contentDisposition, String mimetype, long contentLength) -> {
-            // Show the `DownloadFileDialog` `AlertDialog` and name this instance `@string/download`.
-            AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength);
-            downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
-        });
-
-        // Allow pinch to zoom.
-        mainWebView.getSettings().setBuiltInZoomControls(true);
-
-        // Hide zoom controls.
-        mainWebView.getSettings().setDisplayZoomControls(false);
-
-        // Set `mainWebView` to use a wide viewport.  Otherwise, some web pages will be scrunched and some content will render outside the screen.
-        mainWebView.getSettings().setUseWideViewPort(true);
-
-        // Set `mainWebView` to load in overview mode (zoomed out to the maximum width).
-        mainWebView.getSettings().setLoadWithOverviewMode(true);
-
-        // Explicitly disable geolocation.
-        mainWebView.getSettings().setGeolocationEnabled(false);
-
-        // Initialize cookieManager.
-        cookieManager = CookieManager.getInstance();
-
-        // 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.
-        PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
-
-        // Get the intent that started the app.
-        final Intent launchingIntent = getIntent();
-
-        // Extract the launching intent data as `launchingIntentUriData`.
-        final Uri launchingIntentUriData = launchingIntent.getData();
-
-        // Convert the launching intent URI data (if it exists) to a string and store it in `formattedUrlString`.
-        if (launchingIntentUriData != null) {
-            formattedUrlString = launchingIntentUriData.toString();
-        }
-
-        // Get a handle for the `Runtime`.
-        privacyBrowserRuntime = Runtime.getRuntime();
-
-        // Store the application's private data directory.
-        privateDataDirectoryString = getApplicationInfo().dataDir;
-        // `dataDir` will vary, but will be something like `/data/user/0/com.stoutner.privacybrowser.standard`, which links to `/data/data/com.stoutner.privacybrowser.standard`.
-
-        // 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;
-        nightMode = false;
-
-        // Initialize `webViewTitle`.
-        webViewTitle = getString(R.string.no_title);
-
-        // Initialize `favoriteIconBitmap`.  `ContextCompat` must be used until API >= 21.
-        Drawable favoriteIconDrawable = ContextCompat.getDrawable(getApplicationContext(), R.drawable.world);
-        BitmapDrawable favoriteIconBitmapDrawable = (BitmapDrawable) favoriteIconDrawable;
-        assert favoriteIconBitmapDrawable != null;
-        favoriteIconDefaultBitmap = favoriteIconBitmapDrawable.getBitmap();
-
-        // If the favorite icon is null, load the default.
-        if (favoriteIconBitmap == null) {
-            favoriteIconBitmap = favoriteIconDefaultBitmap;
-        }
-
-        // Apply the app settings from the shared preferences.
-        applyAppSettings();
-
-        // Load `formattedUrlString` if we are not waiting for Orbot to connect.
+        // Load the website if not waiting for Orbot to connect.
         if (!waitingForOrbot) {
             loadUrl(formattedUrlString);
         }
@@ -2109,22 +1335,23 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         // Sets the new intent as the activity intent, so that any future `getIntent()`s pick up this one instead of creating a new activity.
         setIntent(intent);
 
+        // Check to see if the intent contains a new URL.
         if (intent.getData() != null) {
             // Get the intent data and convert it to a string.
             final Uri intentUriData = intent.getData();
             formattedUrlString = intentUriData.toString();
-        }
 
-        // Close the navigation drawer if it is open.
-        if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
-            drawerLayout.closeDrawer(GravityCompat.START);
-        }
+            // Load the website.
+            loadUrl(formattedUrlString);
 
-        // Load the website.
-        loadUrl(formattedUrlString);
+            // Close the navigation drawer if it is open.
+            if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
+                drawerLayout.closeDrawer(GravityCompat.START);
+            }
 
-        // Clear the keyboard if displayed and remove the focus on the urlTextBar if it has it.
-        mainWebView.requestFocus();
+            // Clear the keyboard if displayed and remove the focus on the urlTextBar if it has it.
+            mainWebView.requestFocus();
+        }
     }
 
     @Override
@@ -2132,31 +1359,31 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         // Run the default commands.
         super.onRestart();
 
-        // Apply the app settings, which may have been changed in `SettingsActivity`.
-        applyAppSettings();
-
-        // Apply the domain settings if returning from the Domains Activity.
-        if (reapplyDomainSettingsOnRestart) {
-            // Reset `reapplyDomainSettingsOnRestart`.
-            reapplyDomainSettingsOnRestart = false;
+        // Apply the app settings if returning from the Settings activity..
+        if (reapplyAppSettingsOnRestert) {
+            // Apply the app settings.
+            applyAppSettings();
 
-            // Reapply the domain settings.
-            applyDomainSettings(formattedUrlString);
-        }
+            // Reload the webpage if displaying of images has been disabled in the Settings activity.
+            if (reloadOnRestart) {
+                // Reload `mainWebView`.
+                mainWebView.reload();
 
-        // Update the privacy icon.  `true` runs `invalidateOptionsMenu` as the last step.
-        updatePrivacyIcons(true);
+                // Reset `reloadOnRestartBoolean`.
+                reloadOnRestart = false;
+            }
 
-        // Set the display webpage images mode.
-        setDisplayWebpageImages();
+            // Reset the return from settings flag.
+            reapplyAppSettingsOnRestert = false;
+        }
 
-        // Reload the webpage if displaying of images has been disabled in `SettingsFragment`.
-        if (reloadOnRestart) {
-            // Reload `mainWebView`.
-            mainWebView.reload();
+        // Apply the domain settings if returning from the Domains activity.
+        if (reapplyDomainSettingsOnRestart) {
+            // Reapply the domain settings.
+            applyDomainSettings(formattedUrlString, false);
 
-            // Reset `reloadOnRestartBoolean`.
-            reloadOnRestart = false;
+            // Reset `reapplyDomainSettingsOnRestart`.
+            reapplyDomainSettingsOnRestart = false;
         }
 
         // Load the URL on restart to apply changes to night mode.
@@ -2168,7 +1395,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
             loadUrlOnRestart = false;
         }
 
-        //
+        // Update the bookmarks drawer if returning from the Bookmarks activity.
         if (restartFromBookmarksActivity) {
             // Close the bookmarks drawer.
             drawerLayout.closeDrawer(GravityCompat.END);
@@ -2179,6 +1406,9 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
             // Reset `restartFromBookmarksActivity`.
             restartFromBookmarksActivity = false;
         }
+
+        // Update the privacy icon.  `true` runs `invalidateOptionsMenu` as the last step.  This can be important if the screen was rotated.
+        updatePrivacyIcons(true);
     }
 
     // `onResume()` runs after `onStart()`, which runs after `onCreate()` and `onRestart()`.
@@ -2792,7 +2022,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                 break;
 
             case R.id.domains:
-                // Reapply the domain settings on returning to `MainWebViewActivity`.
+                // Set the flag to reapply the domain settings on restart when returning from Domain Settings.
                 reapplyDomainSettingsOnRestart = true;
                 currentDomainName = "";
 
@@ -2802,7 +2032,10 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                 break;
 
             case R.id.settings:
-                // Reapply the domain settings on returning to `MainWebViewActivity`.
+                // Set the flag to reapply app settings on restart when returning from Settings.
+                reapplyAppSettingsOnRestert = true;
+
+                // Set the flag to reapply the domain settings on restart when returning from Settings.
                 reapplyDomainSettingsOnRestart = true;
                 currentDomainName = "";
 
@@ -3599,7 +2832,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
 
     private void loadUrl(String url) {
         // Apply any custom domain settings.
-        applyDomainSettings(url);
+        applyDomainSettings(url, true);
 
         // Load the URL.
         mainWebView.loadUrl(url, customHeaders);
@@ -3646,7 +2879,10 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         String torSearchCustomURLString = sharedPreferences.getString("tor_search_custom_url", "");
         String searchString = sharedPreferences.getString("search", "https://duckduckgo.com/html/?q=");
         String searchCustomURLString = sharedPreferences.getString("search_custom_url", "");
-        adBlockerEnabled = sharedPreferences.getBoolean("block_ads", true);
+        easyListEnabled = sharedPreferences.getBoolean("easylist", true);
+        easyPrivacyEnabled = sharedPreferences.getBoolean("easyprivacy", true);
+        fanboyAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboy_annoyance_list", true);
+        fanboySocialBlockingListEnabled = sharedPreferences.getBoolean("fanboy_social_blocking_list", true);
         incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
         boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
         boolean proxyThroughOrbot = sharedPreferences.getBoolean("proxy_through_orbot", false);
@@ -3745,7 +2981,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
 
                 /* 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 rehides them after they are shown.
+                 * 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.
@@ -3792,9 +3028,10 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         }
     }
 
-    // We have to use the deprecated `.getDrawable()` until the minimum API >= 21.
+    //
+    // The deprecated `.getDrawable()` must be used until the minimum API >= 21.
     @SuppressWarnings("deprecation")
-    private void applyDomainSettings(String url) {
+    private void applyDomainSettings(String url, boolean resetFavoriteIcon) {
         // Reset `navigatingHistory`.
         navigatingHistory = false;
 
@@ -3816,7 +3053,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
             loadingNewDomainName = !hostName.equals(currentDomainName);
         }
 
-        // Only apply the domain settings if we are loading a new domain.  This allows the user to set temporary settings for JavaScript, cookies, DOM storage, etc.
+        // 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`.
             currentDomainName = hostName;
@@ -3824,9 +3061,11 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
             // Reset `ignorePinnedSslCertificate`.
             ignorePinnedSslCertificate = false;
 
-            // Reset `favoriteIconBitmap` and display it in the `appbar`.
-            favoriteIconBitmap = favoriteIconDefaultBitmap;
-            favoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(favoriteIconBitmap, 64, 64, true));
+            // Reset the favorite icon if specified.
+            if (resetFavoriteIcon) {
+                favoriteIconBitmap = favoriteIconDefaultBitmap;
+                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`.