X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FMainWebViewActivity.java;h=c4715ec5a0fb4ae144dcf1c0c6a5f24e86956f32;hp=d330771672ccf83bd48954a1f8ca979dc0a2db48;hb=6849b42dea845a29d5cfbc709b25b134846754e9;hpb=b1e8283303ee9f0804218058514a6299445b3af3 diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java index d3307716..c4715ec5 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -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 mainWhiteList = new LinkedList<>(); - List multiEntryWhiteList = new LinkedList<>(); - List domainWhiteList = new LinkedList<>(); - List thirdPartyDomainWhiteList = new LinkedList<>(); - List mainBlockList = new LinkedList<>(); - List initialBlockList = new LinkedList<>(); - List finalBlockList = new LinkedList<>(); - List thirdPartyBlockList = new LinkedList<>(); - List thirdPartyInitialBlockList = new LinkedList<>(); - List multiEntryBlockList = new LinkedList<>(); - List multiEntryInitialBlockList = new LinkedList<>(); - List multiEntryFinalBlockList = new LinkedList<>(); - List domainBlockList = new LinkedList<>(); - List domainInitialBlockList = new LinkedList<>(); - List domainFinalBlockList = new LinkedList<>(); - List domainRegularExpressionBlockList = new LinkedList<>(); - List thirdPartyMultiEntryBlockList = new LinkedList<>(); - List thirdPartyMultiEntryInitialBlockList = new LinkedList<>(); - List thirdPartyDomainBlockList = new LinkedList<>(); - List regularExpressionBlockList = new LinkedList<>(); - List 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> easyList = blockListHelper.parseBlockList(getAssets(), "blocklists/easylist.txt"); + final ArrayList> easyPrivacy = blockListHelper.parseBlockList(getAssets(), "blocklists/easyprivacy.txt"); + final ArrayList> fanboyAnnoyance = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-annoyance.txt"); + final ArrayList> 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`.