import androidx.fragment.app.DialogFragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.viewpager.widget.ViewPager;
+import androidx.webkit.WebSettingsCompat;
+import androidx.webkit.WebViewFeature;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data); // Form data can be removed once the minimum API >= 26.
MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data); // Form data can be removed once the minimum API >= 26.
MenuItem refreshMenuItem = menu.findItem(R.id.refresh);
+ MenuItem darkWebViewMenuItem = menu.findItem(R.id.dark_webview);
MenuItem adConsentMenuItem = menu.findItem(R.id.ad_consent);
// Only display third-party cookies if API >= 21
// Disable the clear form data menu item if the API >= 26 so that the status of the main Clear Data is calculated correctly.
clearFormDataMenuItem.setEnabled(Build.VERSION.SDK_INT < 26);
+ // Only display the dark WebView menu item if API >= 21.
+ darkWebViewMenuItem.setVisible(Build.VERSION.SDK_INT >= 21);
+
// Only show Ad Consent if this is the free flavor.
adConsentMenuItem.setVisible(BuildConfig.FLAVOR.contentEquals("free"));
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 darkWebViewMenuItem = menu.findItem(R.id.dark_webview);
// Get a handle for the cookie manager.
CookieManager cookieManager = CookieManager.getInstance();
swipeToRefreshMenuItem.setChecked(currentWebView.getSwipeToRefresh());
wideViewportMenuItem.setChecked(currentWebView.getSettings().getUseWideViewPort());
displayImagesMenuItem.setChecked(currentWebView.getSettings().getLoadsImagesAutomatically());
- nightModeMenuItem.setChecked(currentWebView.getNightMode());
// Initialize the display names for the blocklists with the number of blocked requests.
blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + currentWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
// Enable DOM Storage if JavaScript is enabled.
domStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled());
+
+ // Set the checkbox status for dark WebView if the WebView supports it.
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ darkWebViewMenuItem.setChecked(WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON);
+ }
}
// Set the checked status of the first party cookies menu item.
// Consume the event.
return true;
- case R.id.night_mode:
- // Toggle night mode.
- currentWebView.setNightMode(!currentWebView.getNightMode());
-
- // Enable or disable JavaScript according to night mode, the global preference, and any domain settings.
- if (currentWebView.getNightMode()) { // Night mode is enabled, which requires JavaScript.
- // Enable JavaScript.
- currentWebView.getSettings().setJavaScriptEnabled(true);
- } else if (currentWebView.getDomainSettingsApplied()) { // Night mode is disabled and domain settings are applied. Set JavaScript according to the domain settings.
- // Apply the JavaScript preference that was stored the last time domain settings were loaded.
- currentWebView.getSettings().setJavaScriptEnabled(currentWebView.getDomainSettingsJavaScriptEnabled());
- } else { // Night mode is disabled and domain settings are not applied. Set JavaScript according to the global preference.
- // Apply the JavaScript preference.
- currentWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false));
+ case R.id.dark_webview:
+ // Check to see if dark WebView is supported by this WebView.
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ // Toggle the dark WebView setting.
+ if (WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON) { // Dark WebView is currently enabled.
+ // Turn off dark WebView.
+ WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ } else { // Dark WebView is currently disabled.
+ // turn on dark WebView.
+ WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ }
}
- // Update the privacy icons.
- updatePrivacyIcons(false);
-
- // Reload the website.
- currentWebView.reload();
-
// Consume the event.
return true;
// `reloadWebsite` is used if returning from the Domains activity. Otherwise JavaScript might not function correctly if it is newly enabled.
@SuppressLint("SetJavaScriptEnabled")
- private boolean applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite) {
- // Store a copy of the current user agent to track changes for the return boolean.
- String initialUserAgent = nestedScrollWebView.getSettings().getUserAgentString();
-
+ private void applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite) {
// Store the current URL.
nestedScrollWebView.setCurrentUrl(url);
// Get the settings from the cursor.
nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID)));
- nestedScrollWebView.setDomainSettingsJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
+ nestedScrollWebView.getSettings().setJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
nestedScrollWebView.setAcceptFirstPartyCookies(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1);
boolean domainThirdPartyCookiesEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1);
nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
String userAgentName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
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 webViewThemeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.WEBVIEW_THEME));
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);
pinnedSslEndDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)));
}
+ // 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,
nestedScrollWebView.setPinnedIpAddresses(pinnedHostIpAddresses);
}
- // Set night mode according to the night mode int.
- switch (nightModeInt) {
- case DomainsDatabaseHelper.SYSTEM_DEFAULT:
- // Set night mode according to the current default.
- nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false));
- break;
-
- case DomainsDatabaseHelper.ENABLED:
- // Enable night mode.
- nestedScrollWebView.setNightMode(true);
- break;
-
- case DomainsDatabaseHelper.DISABLED:
- // Disable night mode.
- nestedScrollWebView.setNightMode(false);
- break;
- }
-
- // Enable JavaScript if night mode is enabled.
- if (nestedScrollWebView.getNightMode()) {
- // Enable JavaScript.
- nestedScrollWebView.getSettings().setJavaScriptEnabled(true);
- } else {
- // Set JavaScript according to the domain settings.
- nestedScrollWebView.getSettings().setJavaScriptEnabled(nestedScrollWebView.getDomainSettingsJavaScriptEnabled());
- }
-
- // Close the current host domain settings cursor.
- currentDomainSettingsCursor.close();
-
- // Apply the domain settings.
+ // Apply the cookie domain settings.
cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies());
// Set third-party cookies status if API >= 21.
// Store the swipe to refresh status in the nested scroll WebView.
nestedScrollWebView.setSwipeToRefresh(defaultSwipeToRefresh);
- // Apply swipe to refresh according to the default. This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener.
- swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
+ // Update the swipe refresh layout.
+ if (defaultSwipeToRefresh) { // Swipe to refresh is enabled.
+ // 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);
+ }
break;
case DomainsDatabaseHelper.ENABLED:
// Store the swipe to refresh status in the nested scroll WebView.
nestedScrollWebView.setSwipeToRefresh(true);
- // Enable swipe to refresh. This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener.
- 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);
break;
case DomainsDatabaseHelper.DISABLED:
// Store the swipe to refresh status in the nested scroll WebView.
nestedScrollWebView.setSwipeToRefresh(false);
- // Disable swipe to refresh. This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener.
+ // Disable swipe to refresh.
swipeRefreshLayout.setEnabled(false);
}
+ // Check to see if WebView themes are supported.
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ // Set the WebView theme.
+ switch (webViewThemeInt) {
+ case DomainsDatabaseHelper.SYSTEM_DEFAULT:
+ // // Ge the current system theme status.
+ int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ // Set the WebView theme according to the current system theme status.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { // The system is in night mode.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ } else { // The system is in day mode.
+ // Turn off the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ }
+ break;
+
+ case DomainsDatabaseHelper.LIGHT_THEME:
+ // Turn off the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ break;
+
+ case DomainsDatabaseHelper.DARK_THEME:
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ break;
+ }
+ }
+
// Set the viewport.
switch (wideViewportInt) {
case DomainsDatabaseHelper.SYSTEM_DEFAULT:
}
} else { // The new URL does not have custom domain settings. Load the defaults.
// Store the values from the shared preferences.
- boolean defaultJavaScriptEnabled = sharedPreferences.getBoolean("javascript", false);
+ nestedScrollWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false));
nestedScrollWebView.setAcceptFirstPartyCookies(sharedPreferences.getBoolean("first_party_cookies", false));
boolean defaultThirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false);
nestedScrollWebView.getSettings().setDomStorageEnabled(sharedPreferences.getBoolean("dom_storage", false));
nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, sharedPreferences.getBoolean("ultralist", true));
nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, sharedPreferences.getBoolean("ultraprivacy", true));
nestedScrollWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, sharedPreferences.getBoolean("block_all_third_party_requests", false));
- nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false));
-
- // Enable JavaScript if night mode is enabled.
- if (nestedScrollWebView.getNightMode()) {
- // Enable JavaScript.
- nestedScrollWebView.getSettings().setJavaScriptEnabled(true);
- } else {
- // Set JavaScript according to the domain settings.
- nestedScrollWebView.getSettings().setJavaScriptEnabled(defaultJavaScriptEnabled);
- }
+ String webViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value));
// Apply the default first-party cookie setting.
cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies());
// Store the swipe to refresh status in the nested scroll WebView.
nestedScrollWebView.setSwipeToRefresh(defaultSwipeToRefresh);
- // Apply swipe to refresh according to the default.
- swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
+ // Update the swipe refresh layout.
+ if (defaultSwipeToRefresh) { // Swipe to refresh is enabled.
+ // 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);
+ }
// Reset the pinned variables.
nestedScrollWebView.setDomainSettingsDatabaseId(-1);
nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
}
+ // Get the WebView theme entry values string array.
+ String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values);
+
+ // Apply the WebView theme if supported by the installed WebView.
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ // Set the WebView theme. A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
+ if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) { // The light theme is selected.
+ // Turn off the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ } else { // The system default theme is selected.
+ // Get the current system theme status.
+ int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ // Set the WebView theme according to the current system theme status.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { // The system is in night mode.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ } else { // The system is in day mode.
+ // Turn off the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ }
+ }
+ }
+
// Set the viewport.
nestedScrollWebView.getSettings().setUseWideViewPort(wideViewport);
if (reloadWebsite) {
nestedScrollWebView.reload();
}
-
- // Return the user agent changed status.
- return !nestedScrollWebView.getSettings().getUserAgentString().equals(initialUserAgent);
}
private void applyProxy(boolean reloadWebViews) {
@Override
public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url) {
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Get the WebView theme.
+ String webViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value));
+
+ // Get the WebView theme entry values string array.
+ String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values);
+
+ // Apply the WebView theme if supported by the installed WebView.
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ // Set the WebView theme. A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
+ if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) { // The light theme is selected.
+ // Turn off the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ } else { // The system default theme is selected.
+ // Get the current system theme status.
+ int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ // Set the WebView theme according to the current system theme status.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { // The system is in night mode.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ } else { // The system is in day mode.
+ // Turn off the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ }
+ }
+ }
+
// Get a handle for the app compat delegate.
AppCompatDelegate appCompatDelegate = getDelegate();
// 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);
-
// Initialize the favorite icon.
nestedScrollWebView.initializeFavoriteIcon();
});
// Update the status of swipe to refresh based on the scroll position of the nested scroll WebView. Also reinforce full screen browsing mode.
- // Once the minimum API >= 23 this can be replaced with `nestedScrollWebView.setOnScrollChangeListener()`.
- nestedScrollWebView.getViewTreeObserver().addOnScrollChangedListener(() -> {
- if (nestedScrollWebView.getSwipeToRefresh()) {
- // Only enable swipe to refresh if the WebView is scrolled to the top.
- swipeRefreshLayout.setEnabled(nestedScrollWebView.getScrollY() == 0);
- }
+ // On API < 23, `getViewTreeObserver().addOnScrollChangedListener()` must be used, but it is a little bit buggy and appears to get garbage collected from time to time.
+ if (Build.VERSION.SDK_INT >= 23) {
+ nestedScrollWebView.setOnScrollChangeListener((view, i, i1, i2, i3) -> {
+ if (nestedScrollWebView.getSwipeToRefresh()) {
+ // Only enable swipe to refresh if the WebView is scrolled to the top.
+ swipeRefreshLayout.setEnabled(nestedScrollWebView.getScrollY() == 0);
+ } else {
+ // Disable swipe to refresh.
+ swipeRefreshLayout.setEnabled(false);
+ }
- // Reinforce the system UI visibility flags if in full screen browsing mode.
- // This hides the status and navigation bars, which are displayed if other elements are shown, like dialog boxes, the options menu, or the keyboard.
- if (inFullScreenBrowsingMode) {
- /* 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);
- }
- });
+ // Reinforce the system UI visibility flags if in full screen browsing mode.
+ // This hides the status and navigation bars, which are displayed if other elements are shown, like dialog boxes, the options menu, or the keyboard.
+ if (inFullScreenBrowsingMode) {
+ /* 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 {
+ nestedScrollWebView.getViewTreeObserver().addOnScrollChangedListener(() -> {
+ if (nestedScrollWebView.getSwipeToRefresh()) {
+ // Only enable swipe to refresh if the WebView is scrolled to the top.
+ swipeRefreshLayout.setEnabled(nestedScrollWebView.getScrollY() == 0);
+ } else {
+ // Disable swipe to refresh.
+ swipeRefreshLayout.setEnabled(false);
+ }
+
+
+ // Reinforce the system UI visibility flags if in full screen browsing mode.
+ // This hides the status and navigation bars, which are displayed if other elements are shown, like dialog boxes, the options menu, or the keyboard.
+ if (inFullScreenBrowsingMode) {
+ /* 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);
+ }
+ });
+ }
// Set the web chrome client.
nestedScrollWebView.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 (nestedScrollWebView.getNightMode()) { // Night mode is enabled.
- // `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.
- // `::selection {background: #0D47A1}' sets the text selection highlight color to be a dark blue. `!important` takes precedent over any existing sub-settings.
- nestedScrollWebView.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;} ::selection {background: #0D47A1 !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 gone. This prevents the display of the `WebView` while it is still loading.
- if (progressBar.getVisibility() == View.GONE) {
- nestedScrollWebView.setVisibility(View.VISIBLE);
- }
- };
-
- // Display the WebView after 500 milliseconds.
- displayWebViewHandler.postDelayed(displayWebViewRunnable, 500);
- });
- } else { // Night mode is disabled.
- // Display the nested scroll WebView 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.
- nestedScrollWebView.setVisibility(View.VISIBLE);
- }
-
// Update the progress bar.
progressBar.setProgress(progress);
//Stop the swipe to refresh indicator if it is running
swipeRefreshLayout.setRefreshing(false);
}
+
+ // If this is a new tab, the current WebView would have been created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+ if (progress >= 50) {
+ // Make the current WebView visible.
+ currentWebView.setVisibility(View.VISIBLE);
+ }
}
// Set the favorite icon when it changes.
});
nestedScrollWebView.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.
+ // `shouldOverrideUrlLoading` makes this WebView the default handler for URLs inside the app, so that links are not kicked out to other apps.
// The deprecated `shouldOverrideUrlLoading` must be used until API >= 24.
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// Sanitize the url.
url = sanitizeUrl(url);
+ // Handle the URL according to the type.
if (url.startsWith("http")) { // Load the URL in Privacy Browser.
// Apply the domain settings for the new URL. This doesn't do anything if the domain has not changed.
- boolean userAgentChanged = applyDomainSettings(nestedScrollWebView, url, true, false);
+ applyDomainSettings(nestedScrollWebView, url, true, false);
- // Check if the user agent has changed.
- if (userAgentChanged) {
- // Manually load the URL. The changing of the user agent will cause WebView to reload the previous URL.
- nestedScrollWebView.loadUrl(url, customHeaders);
+ // Load the URL. By using `loadUrl()`, instead of `loadUrlFromBase()`, the Referer header will never be sent.
+ nestedScrollWebView.loadUrl(url, customHeaders);
- // Returning true indicates that Privacy Browser is manually handling the loading of the URL.
- return true;
- } else {
- // Returning false causes the current WebView to handle the URL and prevents it from adding redirects to the history list.
- return false;
- }
+ // Returning true indicates that Privacy Browser is manually handling the loading of the URL.
+ // Custom headers cannot be added if false is returned and the WebView handles the loading of the URL.
+ return true;
} else if (url.startsWith("mailto:")) { // Load the email address in an external email program.
// Use `ACTION_SENDTO` instead of `ACTION_SEND` so that only email programs are launched.
Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
// Reset the requests counters.
nestedScrollWebView.resetRequestsCounters();
- // TODO. Make the background of a new tab match the theme.
- // If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
- if (nestedScrollWebView.getNightMode()) {
- nestedScrollWebView.setVisibility(View.INVISIBLE);
- } else {
- nestedScrollWebView.setVisibility(View.VISIBLE);
- }
-
// Hide the keyboard.
inputMethodManager.hideSoftInputFromWindow(nestedScrollWebView.getWindowToken(), 0);