import com.stoutner.privacybrowser.R;
import com.stoutner.privacybrowser.adapters.WebViewPagerAdapter;
import com.stoutner.privacybrowser.asynctasks.GetHostIpAddresses;
+import com.stoutner.privacybrowser.asynctasks.PopulateBlocklists;
import com.stoutner.privacybrowser.dialogs.AdConsentDialog;
import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog;
import com.stoutner.privacybrowser.dialogs.CreateBookmarkFolderDialog;
import com.stoutner.privacybrowser.dialogs.ViewSslCertificateDialog;
import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
import com.stoutner.privacybrowser.helpers.AdHelper;
-import com.stoutner.privacybrowser.helpers.BlockListHelper;
+import com.stoutner.privacybrowser.helpers.BlocklistHelper;
import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
import com.stoutner.privacybrowser.helpers.CheckPinnedMismatchHelper;
import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
// AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21.
public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener,
- EditBookmarkFolderDialog.EditBookmarkFolderListener, NavigationView.OnNavigationItemSelectedListener, WebViewTabFragment.NewTabListener {
+ EditBookmarkFolderDialog.EditBookmarkFolderListener, NavigationView.OnNavigationItemSelectedListener, PopulateBlocklists.PopulateBlocklistsListener, WebViewTabFragment.NewTabListener {
// `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`.
public static String orbotStatus;
// The options menu is set in `onCreateOptionsMenu()` and used in `onOptionsItemSelected()`, `updatePrivacyIcons()`, and `initializeWebView()`.
private Menu optionsMenu;
- // The blocklists are populated in `onCreate()` and accessed from `initializeWebView()`.
+ // The blocklists are populated in `finishedPopulatingBlocklists()` and accessed from `initializeWebView()`.
private ArrayList<List<String[]>> easyList;
private ArrayList<List<String[]>> easyPrivacy;
private ArrayList<List<String[]>> fanboysAnnoyanceList;
// The URL sanitizers are set in `applyAppSettings()` and used in `sanitizeUrl()`.
private boolean sanitizeGoogleAnalytics;
+ private boolean sanitizeFacebookClickIds;
+ private boolean sanitizeTwitterAmpRedirects;
// The download strings are used in `onCreate()`, `onRequestPermissionResult()` and `initializeWebView()`.
private String downloadUrl;
// Register `orbotStatusBroadcastReceiver` on `this` context.
this.registerReceiver(orbotStatusBroadcastReceiver, new IntentFilter("org.torproject.android.intent.action.STATUS"));
- // Instantiate the blocklist helper.
- BlockListHelper blockListHelper = new BlockListHelper();
-
- // Parse the block lists.
- easyList = blockListHelper.parseBlockList(getAssets(), "blocklists/easylist.txt");
- easyPrivacy = blockListHelper.parseBlockList(getAssets(), "blocklists/easyprivacy.txt");
- fanboysAnnoyanceList = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-annoyance.txt");
- fanboysSocialList = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-social.txt");
- ultraPrivacy = blockListHelper.parseBlockList(getAssets(), "blocklists/ultraprivacy.txt");
-
// Get handles for views that need to be modified.
DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
NavigationView navigationView = findViewById(R.id.navigationview);
FloatingActionButton createBookmarkFab = findViewById(R.id.create_bookmark_fab);
EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext);
+ // Initially disable the sliding drawers. They will be enabled once the blocklists are loaded.
+ drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
+
// Listen for touches on the navigation menu.
navigationView.setNavigationItemSelectedListener(this);
// Create a handler to select the tab.
Handler selectTabHandler = new Handler();
- // Create a runnable select the new tab.
+ // Create a runnable to select the tab.
Runnable selectTabRunnable = () -> {
// Get a handle for the tab.
TabLayout.Tab tab = tabLayout.getTabAt(position);
tab.select();
};
- // Select the tab layout after 100 milliseconds, which leaves enough time for a new tab to be created.
- selectTabHandler.postDelayed(selectTabRunnable, 100);
+ // Select the tab layout after 150 milliseconds, which leaves enough time for a new tab to be inflated.
+ selectTabHandler.postDelayed(selectTabRunnable, 150);
}
}
}
});
- // Add the first tab.
- addTab(null);
-
// Set the bookmarks drawer resources according to the theme. This can't be done in the layout due to compatibility issues with the `DrawerLayout` support widget.
// The deprecated `getResources().getDrawable()` must be used until the minimum API >= 21 and and `getResources().getColor()` must be used until the minimum API >= 23.
if (darkTheme) {
bookmarksHeaderTextView.setPadding(drawerHeaderPaddingLeftAndRight, drawerHeaderPaddingTop, drawerHeaderPaddingLeftAndRight, drawerHeaderPaddingBottom);
}
- // Update the navigation menu items.
- navigationBackMenuItem.setEnabled(currentWebView.canGoBack());
- navigationForwardMenuItem.setEnabled(currentWebView.canGoForward());
- navigationHistoryMenuItem.setEnabled((currentWebView.canGoBack() || currentWebView.canGoForward()));
- navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + currentWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+ // Update the navigation menu items if the WebView is not null.
+ if (currentWebView != null) {
+ navigationBackMenuItem.setEnabled(currentWebView.canGoBack());
+ navigationForwardMenuItem.setEnabled(currentWebView.canGoForward());
+ navigationHistoryMenuItem.setEnabled((currentWebView.canGoBack() || currentWebView.canGoForward()));
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + currentWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
- // Hide the keyboard (if displayed).
- inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0);
+ // Hide the keyboard (if displayed).
+ inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0);
+ }
// Clear the focus from from the URL text box and the WebView. This removes any text selection markers and context menus, which otherwise draw above the open drawers.
urlEditText.clearFocus();
// Destroy the bare WebView.
bareWebView.destroy();
+
+ // Populate the blocklists.
+ new PopulateBlocklists(this, this).execute();
}
@Override
MenuItem blockAllThirdPartyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests);
MenuItem fontSizeMenuItem = menu.findItem(R.id.font_size);
MenuItem swipeToRefreshMenuItem = menu.findItem(R.id.swipe_to_refresh);
+ MenuItem wideViewportMenuItem = menu.findItem(R.id.wide_viewport);
MenuItem displayImagesMenuItem = menu.findItem(R.id.display_images);
MenuItem nightModeMenuItem = menu.findItem(R.id.night_mode);
MenuItem proxyThroughOrbotMenuItem = menu.findItem(R.id.proxy_through_orbot);
ultraPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY));
blockAllThirdPartyRequestsMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
swipeToRefreshMenuItem.setChecked(currentWebView.getSwipeToRefresh());
+ wideViewportMenuItem.setChecked(currentWebView.getSettings().getUseWideViewPort());
displayImagesMenuItem.setChecked(currentWebView.getSettings().getLoadsImagesAutomatically());
nightModeMenuItem.setChecked(currentWebView.getNightMode());
// Update the swipe refresh layout.
if (currentWebView.getSwipeToRefresh()) { // Swipe to refresh is enabled.
- if (Build.VERSION.SDK_INT >= 23) { // For API >= 23, the status of the scroll refresh listener is continuously updated by the on scroll change listener.
- // Only enable the swipe refresh layout if the WebView is scrolled to the top.
- swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
- } else { // For API < 23, the swipe refresh layout is always enabled.
- // Enable the swipe refresh layout.
- swipeRefreshLayout.setEnabled(true);
- }
+ // Only enable the swipe refresh layout if the WebView is scrolled to the top. It is updated every time the scroll changes.
+ swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
} else { // Swipe to refresh is disabled.
// Disable the swipe refresh layout.
swipeRefreshLayout.setEnabled(false);
}
return true;
+ case R.id.wide_viewport:
+ // Toggle the viewport.
+ currentWebView.getSettings().setUseWideViewPort(!currentWebView.getSettings().getUseWideViewPort());
+ return true;
+
case R.id.display_images:
if (currentWebView.getSettings().getLoadsImagesAutomatically()) { // Images are currently loaded automatically.
// Disable loading of images.
}, 200);
return true;
+ case R.id.print:
+ // Get a print manager instance.
+ PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
+
+ // Remove the lint error below that print manager might be null.
+ assert printManager != null;
+
+ // Create a print document adapter from the current WebView.
+ PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter();
+
+ // Print the document.
+ printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
+ return true;
+
+ case R.id.add_to_homescreen:
+ // Instantiate the create home screen shortcut dialog.
+ DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), currentWebView.getUrl(),
+ currentWebView.getFavoriteOrDefaultIcon());
+
+ // Show the create home screen shortcut dialog.
+ createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut));
+ return true;
+
case R.id.view_source:
// Create an intent to launch the view source activity.
Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
startActivity(Intent.createChooser(shareIntent, getString(R.string.share_url)));
return true;
- case R.id.print:
- // Get a print manager instance.
- PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
-
- // Remove the lint error below that print manager might be null.
- assert printManager != null;
-
- // Create a print document adapter from the current WebView.
- PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter();
-
- // Print the document.
- printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
- return true;
-
case R.id.open_with_app:
openWithApp(currentWebView.getUrl());
return true;
openWithBrowser(currentWebView.getUrl());
return true;
- case R.id.add_to_homescreen:
- // Instantiate the create home screen shortcut dialog.
- DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), currentWebView.getUrl(),
- currentWebView.getFavoriteOrDefaultIcon());
-
- // Show the create home screen shortcut dialog.
- createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut));
- return true;
-
case R.id.proxy_through_orbot:
// Toggle the proxy through Orbot variable.
proxyThroughOrbot = !proxyThroughOrbot;
assert inputMethodManager != null;
// Hide the keyboard.
- inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0);
+ inputMethodManager.hideSoftInputFromWindow(toolbar.getWindowToken(), 0);
}
private void applyAppSettings() {
incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
+ sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
+ sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
proxyThroughOrbot = sharedPreferences.getBoolean("proxy_through_orbot", false);
fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true);
newHostName = "";
}
- // 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 (!nestedScrollWebView.getCurrentDomainName().equals(newHostName)) {
+ // Apply the domain settings if a new domain is being loaded or if the new domain is blank. This allows the user to set temporary settings for JavaScript, cookies, DOM storage, etc.
+ if (!nestedScrollWebView.getCurrentDomainName().equals(newHostName) || newHostName.equals("")) {
// Set the new host name as the current domain name.
nestedScrollWebView.setCurrentDomainName(newHostName);
String defaultFontSizeString = sharedPreferences.getString("font_size", getString(R.string.font_size_default_value));
String defaultUserAgentName = sharedPreferences.getString("user_agent", getString(R.string.user_agent_default_value));
boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
- boolean displayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true);
boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
+ boolean wideViewport = sharedPreferences.getBoolean("wide_viewport", true);
+ boolean displayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true);
// Get a handle for the cookie manager.
CookieManager cookieManager = CookieManager.getInstance();
int fontSize = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
int swipeToRefreshInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
int nightModeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE));
+ int wideViewportInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.WIDE_VIEWPORT));
int displayWebpageImagesInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
boolean pinnedSslCertificate = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1);
String pinnedSslIssuedToCName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME));
// Set night mode according to the night mode int.
switch (nightModeInt) {
- case DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT:
+ case DomainsDatabaseHelper.SYSTEM_DEFAULT:
// Set night mode according to the current default.
nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false));
break;
- case DomainsDatabaseHelper.NIGHT_MODE_ENABLED:
+ case DomainsDatabaseHelper.ENABLED:
// Enable night mode.
nestedScrollWebView.setNightMode(true);
break;
- case DomainsDatabaseHelper.NIGHT_MODE_DISABLED:
+ case DomainsDatabaseHelper.DISABLED:
// Disable night mode.
nestedScrollWebView.setNightMode(false);
break;
nestedScrollWebView.getSettings().setTextZoom(fontSize);
}
- // Only set the user agent if the webpage is not currently loading. Otherwise, changing the user agent on redirects can cause the original website to reload.
- // <https://redmine.stoutner.com/issues/160>
- if (nestedScrollWebView.getProgress() == 100) { // A URL is not loading.
- // Set the user agent.
- if (userAgentName.equals(getString(R.string.system_default_user_agent))) { // Use the system default user agent.
- // Get the array position of the default user agent name.
- int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
-
- // Set the user agent according to the system default.
- switch (defaultUserAgentArrayPosition) {
- case UNRECOGNIZED_USER_AGENT: // The default user agent name is not on the canonical list.
- // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
- nestedScrollWebView.getSettings().setUserAgentString(defaultUserAgentName);
- break;
-
- case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
- // Set the user agent to `""`, which uses the default value.
- nestedScrollWebView.getSettings().setUserAgentString("");
- break;
-
- case SETTINGS_CUSTOM_USER_AGENT:
- // Set the default custom user agent.
- nestedScrollWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)));
- break;
-
- default:
- // Get the user agent string from the user agent data array
- nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[defaultUserAgentArrayPosition]);
- }
- } else { // Set the user agent according to the stored name.
- // Get the array position of the user agent name.
- int userAgentArrayPosition = userAgentNamesArray.getPosition(userAgentName);
-
- switch (userAgentArrayPosition) {
- case UNRECOGNIZED_USER_AGENT: // The user agent name contains a custom user agent.
- nestedScrollWebView.getSettings().setUserAgentString(userAgentName);
- break;
-
- case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
- // Set the user agent to `""`, which uses the default value.
- nestedScrollWebView.getSettings().setUserAgentString("");
- break;
-
- default:
- // Get the user agent string from the user agent data array.
- nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
- }
+ // Set the user agent.
+ if (userAgentName.equals(getString(R.string.system_default_user_agent))) { // Use the system default user agent.
+ // Get the array position of the default user agent name.
+ int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
+
+ // Set the user agent according to the system default.
+ switch (defaultUserAgentArrayPosition) {
+ case UNRECOGNIZED_USER_AGENT: // The default user agent name is not on the canonical list.
+ // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
+ nestedScrollWebView.getSettings().setUserAgentString(defaultUserAgentName);
+ break;
+
+ case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
+ // Set the user agent to `""`, which uses the default value.
+ nestedScrollWebView.getSettings().setUserAgentString("");
+ break;
+
+ case SETTINGS_CUSTOM_USER_AGENT:
+ // Set the default custom user agent.
+ nestedScrollWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)));
+ break;
+
+ default:
+ // Get the user agent string from the user agent data array
+ nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[defaultUserAgentArrayPosition]);
+ }
+ } else { // Set the user agent according to the stored name.
+ // Get the array position of the user agent name.
+ int userAgentArrayPosition = userAgentNamesArray.getPosition(userAgentName);
+
+ switch (userAgentArrayPosition) {
+ case UNRECOGNIZED_USER_AGENT: // The user agent name contains a custom user agent.
+ nestedScrollWebView.getSettings().setUserAgentString(userAgentName);
+ break;
+
+ case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
+ // Set the user agent to `""`, which uses the default value.
+ nestedScrollWebView.getSettings().setUserAgentString("");
+ break;
+
+ default:
+ // Get the user agent string from the user agent data array.
+ nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
}
}
// Set swipe to refresh.
switch (swipeToRefreshInt) {
- case DomainsDatabaseHelper.SWIPE_TO_REFRESH_SYSTEM_DEFAULT:
+ case DomainsDatabaseHelper.SYSTEM_DEFAULT:
// Store the swipe to refresh status in the nested scroll WebView.
nestedScrollWebView.setSwipeToRefresh(defaultSwipeToRefresh);
swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
break;
- case DomainsDatabaseHelper.SWIPE_TO_REFRESH_ENABLED:
+ case DomainsDatabaseHelper.ENABLED:
// Store the swipe to refresh status in the nested scroll WebView.
nestedScrollWebView.setSwipeToRefresh(true);
swipeRefreshLayout.setEnabled(true);
break;
- case DomainsDatabaseHelper.SWIPE_TO_REFRESH_DISABLED:
+ case DomainsDatabaseHelper.DISABLED:
// Store the swipe to refresh status in the nested scroll WebView.
nestedScrollWebView.setSwipeToRefresh(false);
swipeRefreshLayout.setEnabled(false);
}
+ // Set the viewport.
+ switch (wideViewportInt) {
+ case DomainsDatabaseHelper.SYSTEM_DEFAULT:
+ nestedScrollWebView.getSettings().setUseWideViewPort(wideViewport);
+ break;
+
+ case DomainsDatabaseHelper.ENABLED:
+ nestedScrollWebView.getSettings().setUseWideViewPort(true);
+ break;
+
+ case DomainsDatabaseHelper.DISABLED:
+ nestedScrollWebView.getSettings().setUseWideViewPort(false);
+ break;
+ }
+
// Set the loading of webpage images.
switch (displayWebpageImagesInt) {
- case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_SYSTEM_DEFAULT:
+ case DomainsDatabaseHelper.SYSTEM_DEFAULT:
nestedScrollWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
break;
- case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED:
+ case DomainsDatabaseHelper.ENABLED:
nestedScrollWebView.getSettings().setLoadsImagesAutomatically(true);
break;
- case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED:
+ case DomainsDatabaseHelper.DISABLED:
nestedScrollWebView.getSettings().setLoadsImagesAutomatically(false);
break;
}
cookieManager.setAcceptThirdPartyCookies(nestedScrollWebView, defaultThirdPartyCookiesEnabled);
}
- // Only set the user agent if the webpage is not currently loading. Otherwise, changing the user agent on redirects can cause the original website to reload.
- // <https://redmine.stoutner.com/issues/160>
- if (nestedScrollWebView.getProgress() == 100) { // A URL is not loading.
- // Get the array position of the user agent name.
- int userAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
+ // Get the array position of the user agent name.
+ int userAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
- // Set the user agent.
- switch (userAgentArrayPosition) {
- case UNRECOGNIZED_USER_AGENT: // The default user agent name is not on the canonical list.
- // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
- nestedScrollWebView.getSettings().setUserAgentString(defaultUserAgentName);
- break;
+ // Set the user agent.
+ switch (userAgentArrayPosition) {
+ case UNRECOGNIZED_USER_AGENT: // The default user agent name is not on the canonical list.
+ // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
+ nestedScrollWebView.getSettings().setUserAgentString(defaultUserAgentName);
+ break;
- case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
- // Set the user agent to `""`, which uses the default value.
- nestedScrollWebView.getSettings().setUserAgentString("");
- break;
+ case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
+ // Set the user agent to `""`, which uses the default value.
+ nestedScrollWebView.getSettings().setUserAgentString("");
+ break;
- case SETTINGS_CUSTOM_USER_AGENT:
- // Set the default custom user agent.
- nestedScrollWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)));
- break;
+ case SETTINGS_CUSTOM_USER_AGENT:
+ // Set the default custom user agent.
+ nestedScrollWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)));
+ break;
- default:
- // Get the user agent string from the user agent data array
- nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
- }
+ default:
+ // Get the user agent string from the user agent data array
+ nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
}
+ // Set the viewport.
+ nestedScrollWebView.getSettings().setUseWideViewPort(wideViewport);
+
// Set the loading of webpage images.
nestedScrollWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
}
}
+ // Sanitize Facebook Click IDs.
+ if (sanitizeFacebookClickIds) {
+ // Remove `?fbclid=`.
+ if (url.contains("?fbclid=")) {
+ url = url.substring(0, url.indexOf("?fbclid="));
+ }
+
+ // Remove `&fbclid=`.
+ if (url.contains("&fbclid=")) {
+ url = url.substring(0, url.indexOf("&fbclid="));
+ }
+ }
+
+ // Sanitize Twitter AMP redirects.
+ if (sanitizeTwitterAmpRedirects) {
+ // Remove `?amp=1`.
+ if (url.contains("?amp=1")) {
+ url = url.substring(0, url.indexOf("?amp=1"));
+ }
+ }
+
// Return the sanitized URL.
return url;
}
+ public void finishedPopulatingBlocklists(ArrayList<ArrayList<List<String[]>>> combinedBlocklists) {
+ // Store the blocklists.
+ easyList = combinedBlocklists.get(0);
+ easyPrivacy = combinedBlocklists.get(1);
+ fanboysAnnoyanceList = combinedBlocklists.get(2);
+ fanboysSocialList = combinedBlocklists.get(3);
+ ultraPrivacy = combinedBlocklists.get(4);
+
+ // Add the first tab.
+ addNewTab("");
+ }
+
public void addTab(View view) {
// Add a new tab with a blank URL.
addNewTab("");
EditText urlEditText = findViewById(R.id.url_edittext);
SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
- //Stop the swipe to refresh indicator if it is running
+ // Stop the swipe to refresh indicator if it is running
swipeRefreshLayout.setRefreshing(false);
// Get the WebView tab fragment.
View fragmentView = webViewTabFragment.getView();
// Set the current WebView if the fragment view is not null.
- if (fragmentView != null) {
+ if (fragmentView != null) { // The fragment has been populated.
// Store the current WebView.
currentWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
// Update the status of swipe to refresh.
if (currentWebView.getSwipeToRefresh()) { // Swipe to refresh is enabled.
- if (Build.VERSION.SDK_INT >= 23) { // For API >= 23, swipe refresh layout is continuously updated with an on scroll change listener and only enabled if the WebView is scrolled to the top.
- // Enable the swipe refresh layout if the WebView is scrolled all the way to the top.
- swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
- } else {
- // Enable the swipe refresh layout.
- swipeRefreshLayout.setEnabled(true);
- }
+ // Enable the swipe refresh layout if the WebView is scrolled all the way to the top. It is updated every time the scroll changes.
+ swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
} else { // Swipe to refresh is disabled.
// Disable the swipe refresh layout.
swipeRefreshLayout.setEnabled(false);
} else {
urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent));
}
+ } else { // The fragment has not been populated. Try again in 100 milliseconds.
+ // Create a handler to set the current WebView.
+ Handler setCurrentWebViewHandler = new Handler();
+
+ // Create a runnable to set the current WebView.
+ Runnable setCurrentWebWebRunnable = () -> {
+ // Set the current WebView.
+ setCurrentWebView(pageNumber);
+ };
+
+ // Try setting the current WebView again after 100 milliseconds.
+ setCurrentWebViewHandler.postDelayed(setCurrentWebWebRunnable, 100);
}
}
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
// Instantiate the blocklist helper.
- BlockListHelper blockListHelper = new BlockListHelper();
+ BlocklistHelper blocklistHelper = new BlocklistHelper();
// Remove the lint warning below that the input method manager might be null.
assert inputMethodManager != null;
// Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- // Get the relevant preferences.
- boolean downloadWithExternalApp = sharedPreferences.getBoolean("download_with_external_app", false);
-
// Initialize the favorite icon.
nestedScrollWebView.initializeFavoriteIcon();
nestedScrollWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
}
- // Set the WebView to use a wide viewport. Otherwise, some web pages will be scrunched and some content will render outside the screen.
- nestedScrollWebView.getSettings().setUseWideViewPort(true);
-
// Set the WebView to load in overview mode (zoomed out to the maximum width).
nestedScrollWebView.getSettings().setLoadWithOverviewMode(true);
// Allow the downloading of files.
nestedScrollWebView.setDownloadListener((String downloadUrl, String userAgent, String contentDisposition, String mimetype, long contentLength) -> {
// Check if the download should be processed by an external app.
- if (downloadWithExternalApp) { // Download with an external app.
+ if (sharedPreferences.getBoolean("download_with_external_app", false)) { // Download with an external app.
// Create a download intent. Not specifying the action type will display the maximum number of options.
Intent downloadIntent = new Intent();
// Get the title text view from the tab.
TextView tabTitleTextView = tabView.findViewById(R.id.title_textview);
- // Set the title as the tab text.
- tabTitleTextView.setText(title);
+ // Set the title according to the URL.
+ if (title.equals("about:blank")) {
+ // Set the title to indicate a new tab.
+ tabTitleTextView.setText(R.string.new_tab);
+ } else {
+ // Set the title as the tab text.
+ tabTitleTextView.setText(title);
+ }
}
}
// Check requests against the block lists. The deprecated `shouldInterceptRequest()` must be used until minimum API >= 21.
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
+ // Sanitize the URL.
+ url = sanitizeUrl(url);
+
// Get a handle for the navigation view.
NavigationView navigationView = findViewById(R.id.navigationview);
// Block third-party requests if enabled.
if (isThirdPartyRequest && nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)) {
// Add the result to the resource requests.
- nestedScrollWebView.addResourceRequest(new String[]{BlockListHelper.REQUEST_THIRD_PARTY, url});
+ nestedScrollWebView.addResourceRequest(new String[]{BlocklistHelper.REQUEST_THIRD_PARTY, url});
// Increment the blocked requests counters.
nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS);
// Check UltraPrivacy if it is enabled.
if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY)) {
// Check the URL against UltraPrivacy.
- String[] ultraPrivacyResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, ultraPrivacy);
+ String[] ultraPrivacyResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, ultraPrivacy);
// Process the UltraPrivacy results.
- if (ultraPrivacyResults[0].equals(BlockListHelper.REQUEST_BLOCKED)) { // The resource request matched UltraPrivacy's blacklist.
+ if (ultraPrivacyResults[0].equals(BlocklistHelper.REQUEST_BLOCKED)) { // The resource request matched UltraPrivacy's blacklist.
// Add the result to the resource requests.
nestedScrollWebView.addResourceRequest(new String[] {ultraPrivacyResults[0], ultraPrivacyResults[1], ultraPrivacyResults[2], ultraPrivacyResults[3], ultraPrivacyResults[4],
ultraPrivacyResults[5]});
// The resource request was blocked. Return an empty web resource response.
return emptyWebResourceResponse;
- } else if (ultraPrivacyResults[0].equals(BlockListHelper.REQUEST_ALLOWED)) { // The resource request matched UltraPrivacy's whitelist.
+ } else if (ultraPrivacyResults[0].equals(BlocklistHelper.REQUEST_ALLOWED)) { // The resource request matched UltraPrivacy's whitelist.
// Add a whitelist entry to the resource requests array.
nestedScrollWebView.addResourceRequest(new String[] {ultraPrivacyResults[0], ultraPrivacyResults[1], ultraPrivacyResults[2], ultraPrivacyResults[3], ultraPrivacyResults[4],
ultraPrivacyResults[5]});
// Check EasyList if it is enabled.
if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.EASY_LIST)) {
// Check the URL against EasyList.
- String[] easyListResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, easyList);
+ String[] easyListResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, easyList);
// Process the EasyList results.
- if (easyListResults[0].equals(BlockListHelper.REQUEST_BLOCKED)) { // The resource request matched EasyList's blacklist.
+ if (easyListResults[0].equals(BlocklistHelper.REQUEST_BLOCKED)) { // The resource request matched EasyList's blacklist.
// Add the result to the resource requests.
nestedScrollWebView.addResourceRequest(new String[] {easyListResults[0], easyListResults[1], easyListResults[2], easyListResults[3], easyListResults[4], easyListResults[5]});
// The resource request was blocked. Return an empty web resource response.
return emptyWebResourceResponse;
- } else if (easyListResults[0].equals(BlockListHelper.REQUEST_ALLOWED)) { // The resource request matched EasyList's whitelist.
+ } else if (easyListResults[0].equals(BlocklistHelper.REQUEST_ALLOWED)) { // The resource request matched EasyList's whitelist.
// Update the whitelist result string array tracker.
whitelistResultStringArray = new String[] {easyListResults[0], easyListResults[1], easyListResults[2], easyListResults[3], easyListResults[4], easyListResults[5]};
}
// Check EasyPrivacy if it is enabled.
if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.EASY_PRIVACY)) {
// Check the URL against EasyPrivacy.
- String[] easyPrivacyResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, easyPrivacy);
+ String[] easyPrivacyResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, easyPrivacy);
// Process the EasyPrivacy results.
- if (easyPrivacyResults[0].equals(BlockListHelper.REQUEST_BLOCKED)) { // The resource request matched EasyPrivacy's blacklist.
+ if (easyPrivacyResults[0].equals(BlocklistHelper.REQUEST_BLOCKED)) { // The resource request matched EasyPrivacy's blacklist.
// Add the result to the resource requests.
nestedScrollWebView.addResourceRequest(new String[] {easyPrivacyResults[0], easyPrivacyResults[1], easyPrivacyResults[2], easyPrivacyResults[3], easyPrivacyResults[4],
easyPrivacyResults[5]});
// The resource request was blocked. Return an empty web resource response.
return emptyWebResourceResponse;
- } else if (easyPrivacyResults[0].equals(BlockListHelper.REQUEST_ALLOWED)) { // The resource request matched EasyPrivacy's whitelist.
+ } else if (easyPrivacyResults[0].equals(BlocklistHelper.REQUEST_ALLOWED)) { // The resource request matched EasyPrivacy's whitelist.
// Update the whitelist result string array tracker.
whitelistResultStringArray = new String[] {easyPrivacyResults[0], easyPrivacyResults[1], easyPrivacyResults[2], easyPrivacyResults[3], easyPrivacyResults[4], easyPrivacyResults[5]};
}
// Check Fanboy’s Annoyance List if it is enabled.
if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)) {
// Check the URL against Fanboy's Annoyance List.
- String[] fanboysAnnoyanceListResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, fanboysAnnoyanceList);
+ String[] fanboysAnnoyanceListResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, fanboysAnnoyanceList);
// Process the Fanboy's Annoyance List results.
- if (fanboysAnnoyanceListResults[0].equals(BlockListHelper.REQUEST_BLOCKED)) { // The resource request matched Fanboy's Annoyance List's blacklist.
+ if (fanboysAnnoyanceListResults[0].equals(BlocklistHelper.REQUEST_BLOCKED)) { // The resource request matched Fanboy's Annoyance List's blacklist.
// Add the result to the resource requests.
nestedScrollWebView.addResourceRequest(new String[] {fanboysAnnoyanceListResults[0], fanboysAnnoyanceListResults[1], fanboysAnnoyanceListResults[2], fanboysAnnoyanceListResults[3],
fanboysAnnoyanceListResults[4], fanboysAnnoyanceListResults[5]});
// The resource request was blocked. Return an empty web resource response.
return emptyWebResourceResponse;
- } else if (fanboysAnnoyanceListResults[0].equals(BlockListHelper.REQUEST_ALLOWED)){ // The resource request matched Fanboy's Annoyance List's whitelist.
+ } else if (fanboysAnnoyanceListResults[0].equals(BlocklistHelper.REQUEST_ALLOWED)){ // The resource request matched Fanboy's Annoyance List's whitelist.
// Update the whitelist result string array tracker.
whitelistResultStringArray = new String[] {fanboysAnnoyanceListResults[0], fanboysAnnoyanceListResults[1], fanboysAnnoyanceListResults[2], fanboysAnnoyanceListResults[3],
fanboysAnnoyanceListResults[4], fanboysAnnoyanceListResults[5]};
}
} else if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST)) { // Only check Fanboy’s Social Blocking List if Fanboy’s Annoyance List is disabled.
// Check the URL against Fanboy's Annoyance List.
- String[] fanboysSocialListResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, fanboysSocialList);
+ String[] fanboysSocialListResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, fanboysSocialList);
// Process the Fanboy's Social Blocking List results.
- if (fanboysSocialListResults[0].equals(BlockListHelper.REQUEST_BLOCKED)) { // The resource request matched Fanboy's Social Blocking List's blacklist.
+ if (fanboysSocialListResults[0].equals(BlocklistHelper.REQUEST_BLOCKED)) { // The resource request matched Fanboy's Social Blocking List's blacklist.
// Add the result to the resource requests.
nestedScrollWebView.addResourceRequest(new String[] {fanboysSocialListResults[0], fanboysSocialListResults[1], fanboysSocialListResults[2], fanboysSocialListResults[3],
fanboysSocialListResults[4], fanboysSocialListResults[5]});
// The resource request was blocked. Return an empty web resource response.
return emptyWebResourceResponse;
- } else if (fanboysSocialListResults[0].equals(BlockListHelper.REQUEST_ALLOWED)) { // The resource request matched Fanboy's Social Blocking List's whitelist.
+ } else if (fanboysSocialListResults[0].equals(BlocklistHelper.REQUEST_ALLOWED)) { // The resource request matched Fanboy's Social Blocking List's whitelist.
// Update the whitelist result string array tracker.
whitelistResultStringArray = new String[] {fanboysSocialListResults[0], fanboysSocialListResults[1], fanboysSocialListResults[2], fanboysSocialListResults[3],
fanboysSocialListResults[4], fanboysSocialListResults[5]};
if (whitelistResultStringArray != null) { // The request was processed by a whitelist.
nestedScrollWebView.addResourceRequest(whitelistResultStringArray);
} else { // The request didn't match any blocklist entry. Log it as a default request.
- nestedScrollWebView.addResourceRequest(new String[]{BlockListHelper.REQUEST_DEFAULT, url});
+ nestedScrollWebView.addResourceRequest(new String[]{BlocklistHelper.REQUEST_DEFAULT, url});
}
// The resource request has not been blocked. `return null` loads the requested resource.
}
}
- // Replace Refresh with Stop if the options menu has been created. (The WebView typically begins loading before the menu items are instantiated.)
+ // Replace Refresh with Stop if the options menu has been created. (The first WebView typically begins loading before the menu items are instantiated.)
if (optionsMenu != null) {
// Get a handle for the refresh menu item.
MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh);
@Override
public void onPageFinished(WebView view, String url) {
- // Reset the wide view port if it has been turned off by the waiting for Orbot message.
- if (!waitingForOrbot) {
- // Only use a wide view port if the URL starts with `http`, not for `file://` and `content://`.
- nestedScrollWebView.getSettings().setUseWideViewPort(url.startsWith("http"));
- }
-
// Flush any cookies to persistent storage. The cookie manager has become very lazy about flushing cookies in recent versions.
if (nestedScrollWebView.getAcceptFirstPartyCookies() && Build.VERSION.SDK_INT >= 21) {
CookieManager.getInstance().flush();
// Get the current URL from the nested scroll WebView. This is more accurate than using the URL passed into the method, which is sometimes not the final one.
String currentUrl = nestedScrollWebView.getUrl();
+ // Get the current tab.
+ TabLayout.Tab tab = tabLayout.getTabAt(currentPagePosition);
+
// Update the URL text bar if the page is currently selected and the user is not currently typing in the URL edit text.
// Crash records show that, in some crazy way, it is possible for the current URL to be blank at this point.
// Probably some sort of race condition when Privacy Browser is being resumed.
// Display the keyboard.
inputMethodManager.showSoftInput(urlEditText, 0);
- // Hide the WebView, which causes the default background color to be displayed according to the theme.
- nestedScrollWebView.setVisibility(View.INVISIBLE);
-
// Apply the domain settings. This clears any settings from the previous domain.
applyDomainSettings(nestedScrollWebView, "", true, false);
+
+ // Only populate the title text view if the tab has been fully created.
+ if (tab != null) {
+ // Get the custom view from the tab.
+ View tabView = tab.getCustomView();
+
+ // Remove the incorrect warning below that the current tab view might be null.
+ assert tabView != null;
+
+ // Get the title text view from the tab.
+ TextView tabTitleTextView = tabView.findViewById(R.id.title_textview);
+
+ // Set the title as the tab text.
+ tabTitleTextView.setText(R.string.new_tab);
+ }
} else { // The WebView has loaded a webpage.
// Display the final URL. Getting the URL from the WebView instead of using the one provided by `onPageFinished()` makes websites like YouTube function correctly.
urlEditText.setText(currentUrl);
// Apply text highlighting to the URL.
highlightUrlText();
- }
- }
-
- // Get the current tab.
- TabLayout.Tab tab = tabLayout.getTabAt(currentPagePosition);
- // Only populate the title text view if the tab has been fully created.
- if (tab != null) {
- // Get the custom view from the tab.
- View tabView = tab.getCustomView();
+ // Only populate the title text view if the tab has been fully created.
+ if (tab != null) {
+ // Get the custom view from the tab.
+ View tabView = tab.getCustomView();
- // Remove the incorrect warning below that the current tab view might be null.
- assert tabView != null;
+ // Remove the incorrect warning below that the current tab view might be null.
+ assert tabView != null;
- // Get the title text view from the tab.
- TextView tabTitleTextView = tabView.findViewById(R.id.title_textview);
+ // Get the title text view from the tab.
+ TextView tabTitleTextView = tabView.findViewById(R.id.title_textview);
- // Set the title as the tab text. Sometimes `onReceivedTitle()` is not called, especially when navigating history.
- tabTitleTextView.setText(nestedScrollWebView.getTitle());
+ // Set the title as the tab text. Sometimes `onReceivedTitle()` is not called, especially when navigating history.
+ tabTitleTextView.setText(nestedScrollWebView.getTitle());
+ }
+ }
}
}
}
// Load the URL.
nestedScrollWebView.loadUrl(url, customHeaders);
- // Display the keyboard if the URL is blank.
+ // Set the focus and display the keyboard if the URL is blank.
if (url.equals("")) {
- inputMethodManager.showSoftInput(urlEditText, 0);
+ // Request focus for the URL text box.
+ urlEditText.requestFocus();
+
+ // Create a display keyboard handler.
+ Handler displayKeyboardHandler = new Handler();
+
+ // Create a display keyboard runnable.
+ Runnable displayKeyboardRunnable = () -> {
+ // Display the keyboard.
+ inputMethodManager.showSoftInput(urlEditText, 0);
+ };
+
+ // Display the keyboard after 100 milliseconds, which leaves enough time for the tab to transition.
+ displayKeyboardHandler.postDelayed(displayKeyboardRunnable, 100);
}
}
}