+ // Show the action bar.
+ actionBar.show();
+ }
+
+ /* Hide the system bars.
+ * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
+ * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
+ * 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.
+ */
+ rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+ } else { // Privacy Browser is not in full screen browsing mode.
+ // Reset the full screen tracker, which could be true if Privacy Browser was in full screen mode before entering settings and full screen browsing was disabled.
+ inFullScreenBrowsingMode = false;
+
+ // Show the tab linear layout.
+ tabsLinearLayout.setVisibility(View.VISIBLE);
+
+ // Show the action bar.
+ actionBar.show();
+
+ // Remove the `SYSTEM_UI` flags from the root frame layout.
+ rootFrameLayout.setSystemUiVisibility(0);
+ }
+ }
+
+ @Override
+ public void navigateHistory(@NonNull String url, int steps) {
+ // Apply the domain settings.
+ applyDomainSettings(currentWebView, url, false, false, false);
+
+ // Load the history entry.
+ currentWebView.goBackOrForward(steps);
+ }
+
+ @Override
+ public void pinnedErrorGoBack() {
+ // Get the current web back forward list.
+ WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
+
+ // Get the previous entry URL.
+ String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
+
+ // Apply the domain settings.
+ applyDomainSettings(currentWebView, previousUrl, false, false, false);
+
+ // Go back.
+ currentWebView.goBack();
+ }
+
+ // `reloadWebsite` is used if returning from the Domains activity. Otherwise JavaScript might not function correctly if it is newly enabled.
+ @SuppressLint("SetJavaScriptEnabled")
+ private void applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite, boolean loadUrl) {
+ // Store the current URL.
+ nestedScrollWebView.setCurrentUrl(url);
+
+ // Parse the URL into a URI.
+ Uri uri = Uri.parse(url);
+
+ // Extract the domain from `uri`.
+ String newHostName = uri.getHost();
+
+ // Strings don't like to be null.
+ if (newHostName == null) {
+ 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);
+
+ // Reset the ignoring of pinned domain information.
+ nestedScrollWebView.setIgnorePinnedDomainInformation(false);
+
+ // Clear any pinned SSL certificate or IP addresses.
+ nestedScrollWebView.clearPinnedSslCertificate();
+ nestedScrollWebView.setPinnedIpAddresses("");
+
+ // Reset the favorite icon if specified.
+ if (resetTab) {
+ // Initialize the favorite icon.
+ nestedScrollWebView.initializeFavoriteIcon();
+
+ // Get the current page position.
+ int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
+
+ // Get the corresponding tab.
+ TabLayout.Tab tab = tabLayout.getTabAt(currentPagePosition);
+
+ // Update the tab if it isn't null, which sometimes happens when restarting from the background.
+ if (tab != null) {
+ // Get the tab custom view.
+ View tabCustomView = tab.getCustomView();
+
+ // Remove the warning below that the tab custom view might be null.
+ assert tabCustomView != null;
+
+ // Get the tab views.
+ ImageView tabFavoriteIconImageView = tabCustomView.findViewById(R.id.favorite_icon_imageview);
+ TextView tabTitleTextView = tabCustomView.findViewById(R.id.title_textview);
+
+ // Set the default favorite icon as the favorite icon for this tab.
+ tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(nestedScrollWebView.getFavoriteOrDefaultIcon(), 64, 64, true));
+
+ // Set the loading title text.
+ tabTitleTextView.setText(R.string.loading);
+ }
+ }
+
+ // Get a full domain name cursor.
+ Cursor domainNameCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomain();
+
+ // Initialize `domainSettingsSet`.
+ Set<String> domainSettingsSet = new HashSet<>();
+
+ // Get the domain name column index.
+ int domainNameColumnIndex = domainNameCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME);
+
+ // Populate the domain settings set.
+ for (int i = 0; i < domainNameCursor.getCount(); i++) {
+ // Move the domains cursor to the current row.
+ domainNameCursor.moveToPosition(i);
+
+ // Store the domain name in the domain settings set.
+ domainSettingsSet.add(domainNameCursor.getString(domainNameColumnIndex));
+ }
+
+ // Close the domain name cursor.
+ domainNameCursor.close();
+
+ // Initialize the domain name in database variable.
+ String domainNameInDatabase = null;
+
+ // Check the hostname against the domain settings set.
+ if (domainSettingsSet.contains(newHostName)) { // The hostname is contained in the domain settings set.
+ // Record the domain name in the database.
+ domainNameInDatabase = newHostName;
+
+ // Set the domain settings applied tracker to true.
+ nestedScrollWebView.setDomainSettingsApplied(true);
+ } else { // The hostname is not contained in the domain settings set.
+ // Set the domain settings applied tracker to false.
+ nestedScrollWebView.setDomainSettingsApplied(false);
+ }
+
+ // Check all the subdomains of the host name against wildcard domains in the domain cursor.
+ while (!nestedScrollWebView.getDomainSettingsApplied() && newHostName.contains(".")) { // Stop checking if domain settings are already applied or there are no more `.` in the hostname.
+ if (domainSettingsSet.contains("*." + newHostName)) { // Check the host name prepended by `*.`.
+ // Set the domain settings applied tracker to true.
+ nestedScrollWebView.setDomainSettingsApplied(true);
+
+ // Store the applied domain names as it appears in the database.
+ domainNameInDatabase = "*." + newHostName;
+ }
+
+ // Strip out the lowest subdomain of of the host name.
+ newHostName = newHostName.substring(newHostName.indexOf(".") + 1);
+ }
+
+
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Store the general preference information.
+ String defaultFontSizeString = sharedPreferences.getString(getString(R.string.font_size_key), getString(R.string.font_size_default_value));
+ String defaultUserAgentName = sharedPreferences.getString(getString(R.string.user_agent_key), getString(R.string.user_agent_default_value));
+ boolean defaultSwipeToRefresh = sharedPreferences.getBoolean(getString(R.string.swipe_to_refresh_key), true);
+ String webViewTheme = sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value));
+ boolean wideViewport = sharedPreferences.getBoolean(getString(R.string.wide_viewport_key), true);
+ boolean displayWebpageImages = sharedPreferences.getBoolean(getString(R.string.display_webpage_images_key), true);
+
+ // Get the WebView theme entry values string array.
+ String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values);
+
+ // Get a handle for the cookie manager.
+ CookieManager cookieManager = CookieManager.getInstance();
+
+ // Initialize the user agent array adapter and string array.
+ ArrayAdapter<CharSequence> userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item);
+ String[] userAgentDataArray = getResources().getStringArray(R.array.user_agent_data);
+
+ if (nestedScrollWebView.getDomainSettingsApplied()) { // The url has custom domain settings.
+ // Remove the incorrect lint warning below that the domain name in database might be null.
+ assert domainNameInDatabase != null;
+
+ // Get a cursor for the current host.
+ Cursor currentDomainSettingsCursor = domainsDatabaseHelper.getCursorForDomainName(domainNameInDatabase);
+
+ // Move to the first position.
+ currentDomainSettingsCursor.moveToFirst();
+
+ // Get the settings from the cursor.
+ nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ID)));
+ nestedScrollWebView.getSettings().setJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
+ nestedScrollWebView.setAcceptCookies(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES)) == 1);
+ nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
+ // Form data can be removed once the minimum API >= 26.
+ boolean saveFormData = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1);
+ nestedScrollWebView.setEasyListEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1);
+ nestedScrollWebView.setEasyPrivacyEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1);
+ nestedScrollWebView.setFanboysAnnoyanceListEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(
+ DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1);
+ nestedScrollWebView.setFanboysSocialBlockingListEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(
+ DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1);
+ nestedScrollWebView.setUltraListEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST)) == 1);
+ nestedScrollWebView.setUltraPrivacyEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)) == 1);
+ nestedScrollWebView.setBlockAllThirdPartyRequests(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(
+ DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1);
+ String userAgentName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT));
+ int fontSize = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE));
+ int swipeToRefreshInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
+ int webViewThemeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME));
+ int wideViewportInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WIDE_VIEWPORT));
+ int displayWebpageImagesInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DISPLAY_IMAGES));
+ boolean pinnedSslCertificate = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1);
+ String pinnedSslIssuedToCName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME));
+ String pinnedSslIssuedToOName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION));
+ String pinnedSslIssuedToUName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT));
+ String pinnedSslIssuedByCName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME));
+ String pinnedSslIssuedByOName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION));
+ String pinnedSslIssuedByUName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT));
+ Date pinnedSslStartDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_START_DATE)));
+ Date pinnedSslEndDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_END_DATE)));
+ boolean pinnedIpAddresses = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)) == 1);
+ String pinnedHostIpAddresses = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.IP_ADDRESSES));
+
+ // Close the current host domain settings cursor.
+ currentDomainSettingsCursor.close();
+
+ // If there is a pinned SSL certificate, store it in the WebView.
+ if (pinnedSslCertificate) {
+ nestedScrollWebView.setPinnedSslCertificate(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName,
+ pinnedSslStartDate, pinnedSslEndDate);
+ }
+
+ // If there is a pinned IP address, store it in the WebView.
+ if (pinnedIpAddresses) {
+ nestedScrollWebView.setPinnedIpAddresses(pinnedHostIpAddresses);
+ }
+
+ // Apply the cookie domain settings.
+ cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptCookies());
+
+ // Apply the form data setting if the API < 26.
+ if (Build.VERSION.SDK_INT < 26) {
+ nestedScrollWebView.getSettings().setSaveFormData(saveFormData);
+ }
+
+ // Apply the font size.
+ try { // Try the specified font size to see if it is valid.
+ if (fontSize == 0) { // Apply the default font size.
+ // Try to set the font size from the value in the app settings.
+ nestedScrollWebView.getSettings().setTextZoom(Integer.parseInt(defaultFontSizeString));
+ } else { // Apply the font size from domain settings.
+ nestedScrollWebView.getSettings().setTextZoom(fontSize);
+ }
+ } catch (Exception exception) { // The specified font size is invalid
+ // Set the font size to be 100%
+ nestedScrollWebView.getSettings().setTextZoom(100);
+ }
+
+ // 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(getString(R.string.custom_user_agent_key), 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.SYSTEM_DEFAULT:
+ // Store the swipe to refresh status in the nested scroll WebView.
+ nestedScrollWebView.setSwipeToRefresh(defaultSwipeToRefresh);
+
+ // Update the swipe refresh layout.
+ if (defaultSwipeToRefresh) { // Swipe to refresh is enabled.
+ // Update the status of the swipe refresh layout if the current WebView is not null (crash reports indicate that in some unexpected way it sometimes is null).
+ if (currentWebView != null) {
+ // 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.getScrollY() == 0);