import androidx.viewpager.widget.ViewPager;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;
-import kotlin.Pair;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
import com.stoutner.privacybrowser.helpers.ProxyHelper;
+import com.stoutner.privacybrowser.helpers.SanitizeUrlHelper;
import com.stoutner.privacybrowser.views.NestedScrollWebView;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Date;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import kotlin.Pair;
+
public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, OpenDialog.OpenListener,
PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveDialog.SaveListener, UrlHistoryDialog.NavigateHistoryListener,
public static String proxyMode = ProxyHelper.NONE;
// Declare the public static variables.
- public static String currentBookmarksFolder;
+ public static String currentBookmarksFolder = "";
public static boolean restartFromBookmarksActivity;
public static WebViewPagerAdapter webViewPagerAdapter;
// `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxy()`, and `applyDomainSettings()`.
private NestedScrollWebView currentWebView;
- // `customHeader` is used in `onCreate()`, `onOptionsItemSelected()`, `onCreateContextMenu()`, and `loadUrl()`.
- private final Map<String, String> customHeaders = new HashMap<>();
-
// The search URL is set in `applyAppSettings()` and used in `onNewIntent()`, `loadUrlFromTextBox()`, `initializeApp()`, and `initializeWebView()`.
private String searchURL;
private int defaultProgressViewStartOffset;
private int defaultProgressViewEndOffset;
- // The URL sanitizers are set in `applyAppSettings()` and used in `sanitizeUrl()`.
- private boolean sanitizeGoogleAnalytics;
- private boolean sanitizeFacebookClickIds;
- private boolean sanitizeTwitterAmpRedirects;
+ // Declare the helpers.
+ private BookmarksDatabaseHelper bookmarksDatabaseHelper;
+ private DomainsDatabaseHelper domainsDatabaseHelper;
+ private ProxyHelper proxyHelper;
+ private SanitizeUrlHelper sanitizeUrlHelper;
// Declare the class variables
- private BookmarksDatabaseHelper bookmarksDatabaseHelper;
private boolean bottomAppBar;
private boolean displayingFullScreenVideo;
private boolean downloadWithExternalApp;
private boolean inFullScreenBrowsingMode;
private boolean loadingNewIntent;
private BroadcastReceiver orbotStatusBroadcastReceiver;
- private ProxyHelper proxyHelper;
private boolean reapplyAppSettingsOnRestart;
private boolean reapplyDomainSettingsOnRestart;
+ private boolean sanitizeAmpRedirects;
+ private boolean sanitizeTrackingQueries;
private boolean scrollAppBar;
private boolean waitingForProxy;
private String webViewDefaultUserAgent;
private Activity resultLauncherActivityHandle;
// Define the save URL activity result launcher. It must be defined before `onCreate()` is run or the app will crash.
- private final ActivityResultLauncher<String> saveUrlActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(),
+ private final ActivityResultLauncher<String> saveUrlActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument("text/*"),
new ActivityResultCallback<Uri>() {
@Override
public void onActivityResult(Uri fileUri) {
});
// Define the save webpage archive activity result launcher. It must be defined before `onCreate()` is run or the app will crash.
- private final ActivityResultLauncher<String> saveWebpageArchiveActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(),
+ private final ActivityResultLauncher<String> saveWebpageArchiveActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument("multipart/related"),
new ActivityResultCallback<Uri>() {
@Override
public void onActivityResult(Uri fileUri) {
});
// Define the save webpage image activity result launcher. It must be defined before `onCreate()` is run or the app will crash.
- private final ActivityResultLauncher<String> saveWebpageImageActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(),
+ private final ActivityResultLauncher<String> saveWebpageImageActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument("image/png"),
new ActivityResultCallback<Uri>() {
@Override
public void onActivityResult(Uri fileUri) {
// Enable the drawing of the entire webpage. This makes it possible to save a website image. This must be done before anything else happens with the WebView.
WebView.enableSlowWholeDocumentDraw();
- // Set the theme.
- setTheme(R.style.PrivacyBrowser);
-
// Set the content view according to the position of the app bar.
if (bottomAppBar) setContentView(R.layout.main_framelayout_bottom_appbar);
else setContentView(R.layout.main_framelayout_top_appbar);
// Store up to 100 tabs in memory.
webViewPager.setOffscreenPageLimit(100);
- // Instantiate the proxy helper.
+ // Instantiate the helpers.
+ bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this);
+ domainsDatabaseHelper = new DomainsDatabaseHelper(this);
proxyHelper = new ProxyHelper();
+ sanitizeUrlHelper = new SanitizeUrlHelper();
// Initialize the app.
initializeApp();
// Run the default commands.
super.onNewIntent(intent);
- // Replace the intent that started the app with this one.
- setIntent(intent);
-
// Check to see if the app is being restarted from a saved state.
if (savedStateArrayList == null || savedStateArrayList.size() == 0) { // The activity is not being restarted from a saved state.
// Get the information from the intent.
drawerLayout.closeDrawer(GravityCompat.END);
}
}
+ } else { // The app has been restarted.
+ // Replace the intent that started the app with this one. This will load the tab after the others have been restored.
+ setIntent(intent);
}
}
// Enable DOM Storage if JavaScript is enabled.
optionsDomStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled());
+ // Get the current theme status.
+ int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ // Enable dark WebView if the API is < 33 or if night mode is enabled.
+ optionsDarkWebViewMenuItem.setEnabled((Build.VERSION.SDK_INT < 33) || (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES));
+
// Set the checkbox status for dark WebView if the WebView supports it.
- if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ if ((Build.VERSION.SDK_INT >= 33) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) { // The device is running API >= 33 and algorithmic darkening is supported.
+ optionsDarkWebViewMenuItem.setChecked(WebSettingsCompat.isAlgorithmicDarkeningAllowed(currentWebView.getSettings()));
+ } else if ((Build.VERSION.SDK_INT < 33) && WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { // The device is running API < 33 and the WebView supports force dark.
+ //noinspection deprecation
optionsDarkWebViewMenuItem.setChecked(WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON);
}
}
return true;
} else if (menuItemId == R.id.dark_webview) { // Dark WebView.
// Check to see if dark WebView is supported by this WebView.
- if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ if ((Build.VERSION.SDK_INT >= 33) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) { // The device is running API >= 33 and algorithmic darkening is supported.
+ // Toggle algorithmic darkening.
+ WebSettingsCompat.setAlgorithmicDarkeningAllowed(currentWebView.getSettings(), !WebSettingsCompat.isAlgorithmicDarkeningAllowed(currentWebView.getSettings()));
+ } else if ((Build.VERSION.SDK_INT < 33) && WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { // The device is running API < 33 and the WebView supports force dark.
// Toggle the dark WebView setting.
+ //noinspection deprecation
if (WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON) { // Dark WebView is currently enabled.
// Turn off dark WebView.
+ //noinspection deprecation
WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
} else { // Dark WebView is currently disabled.
// Turn on dark WebView.
+ //noinspection deprecation
WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
}
}
Uri currentUri = Uri.parse(currentWebView.getUrl());
String currentDomain = currentUri.getHost();
- // Initialize the database handler.
- DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this);
-
// Create the domain and store the database ID.
int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain);
drawerLayout.setDrawerTitle(GravityCompat.START, getString(R.string.navigation_drawer));
drawerLayout.setDrawerTitle(GravityCompat.END, getString(R.string.bookmarks));
- // Initialize the bookmarks database helper.
- bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this);
-
- // Initialize `currentBookmarksFolder`. `""` is the home folder in the database.
- currentBookmarksFolder = "";
-
- // Load the home folder, which is `""` in the database.
+ // Load the bookmarks folder.
loadBookmarksFolder();
+ // Handle clicks on bookmarks.
bookmarksListView.setOnItemClickListener((parent, view, position, id) -> {
// Convert the id from long to int to match the format of the bookmarks database.
int databaseId = (int) id;
}
});
- // 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", "");
-
// Inflate a bare WebView to get the default user agent. It is not used to render content on the screen.
@SuppressLint("InflateParams") View webViewLayout = getLayoutInflater().inflate(R.layout.bare_webview, null, false);
// Store the values from the shared preferences in variables.
incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
- sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
- sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
- sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
+ sanitizeTrackingQueries = sharedPreferences.getBoolean(getString(R.string.tracking_queries_key), true);
+ sanitizeAmpRedirects = sharedPreferences.getBoolean(getString(R.string.amp_redirects_key), true);
proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value));
fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
downloadWithExternalApp = sharedPreferences.getBoolean(getString(R.string.download_with_external_app_key), false);
}
}
- // Initialize the database handler.
- DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this);
-
- // Get a full cursor from `domainsDatabaseHelper`.
+ // Get a full domain name cursor.
Cursor domainNameCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomain();
// Initialize `domainSettingsSet`.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
// Store the general preference information.
+ boolean defaultXRequestedWithHeader = sharedPreferences.getBoolean(getString(R.string.x_requested_with_header_key), true);
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 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.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);
+ nestedScrollWebView.setBlockAllThirdPartyRequests(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(
+ DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1);
String userAgentName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT));
+ int xRequestedWithHeaderInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.X_REQUESTED_WITH_HEADER));
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));
nestedScrollWebView.getSettings().setSaveFormData(saveFormData);
}
+ // Set the X-Requested-With header.
+ switch (xRequestedWithHeaderInt) {
+ case DomainsDatabaseHelper.SYSTEM_DEFAULT:
+ if (defaultXRequestedWithHeader)
+ nestedScrollWebView.setXRequestedWithHeader();
+ else
+ nestedScrollWebView.resetXRequestedWithHeader();
+ break;
+
+ case DomainsDatabaseHelper.ENABLED:
+ nestedScrollWebView.setXRequestedWithHeader();
+ break;
+
+ case DomainsDatabaseHelper.DISABLED:
+ nestedScrollWebView.resetXRequestedWithHeader();
+ break;
+ }
+
// Apply the font size.
try { // Try the specified font size to see if it is valid.
if (fontSize == 0) { // Apply the default font size.
// Disable swipe to refresh.
swipeRefreshLayout.setEnabled(false);
+ break;
}
// Check to see if WebView themes are supported.
- if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ if ((Build.VERSION.SDK_INT >= 33) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) { // The device is running API >= 33 and algorithmic darkening is supported.
+ // Set the WebView theme.
+ switch (webViewThemeInt) {
+ case DomainsDatabaseHelper.SYSTEM_DEFAULT:
+ // 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 algorithmic darkening.
+ WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), false);
+ } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected.
+ // Turn on algorithmic darkening.
+ WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), true);
+ } 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 algorithmic darkening according to the current system theme status.
+ WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES));
+ }
+ break;
+
+ case DomainsDatabaseHelper.LIGHT_THEME:
+ // Turn off algorithmic darkening.
+ WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), false);
+ break;
+
+ case DomainsDatabaseHelper.DARK_THEME:
+ // Turn on algorithmic darkening.
+ WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), true);
+ break;
+ }
+ } else if ((Build.VERSION.SDK_INT < 33) && WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { // The device is running API < 33 and the WebView supports force dark.
// Set the WebView theme.
switch (webViewThemeInt) {
case DomainsDatabaseHelper.SYSTEM_DEFAULT:
// 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.
+ //noinspection deprecation
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.
+ //noinspection deprecation
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
} else { // The system default theme is selected.
// Get the current system theme status.
// Set the WebView theme according to the current system theme status.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode.
// Turn off the WebView dark mode.
+ //noinspection deprecation
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
} else { // The system is in night mode.
// Turn on the WebView dark mode.
+ //noinspection deprecation
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
}
}
case DomainsDatabaseHelper.LIGHT_THEME:
// Turn off the WebView dark mode.
+ //noinspection deprecation
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
break;
case DomainsDatabaseHelper.DARK_THEME:
// Turn on the WebView dark mode.
+ //noinspection deprecation
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
break;
}
nestedScrollWebView.getSettings().setSaveFormData(saveFormData);
}
+ // Store the X-Requested-With header status in the nested scroll WebView.
+ if (defaultXRequestedWithHeader)
+ nestedScrollWebView.setXRequestedWithHeader();
+ else
+ nestedScrollWebView.resetXRequestedWithHeader();
+
// Store the swipe to refresh status in the nested scroll WebView.
nestedScrollWebView.setSwipeToRefresh(defaultSwipeToRefresh);
}
// Apply the WebView theme if supported by the installed WebView.
- if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ if ((Build.VERSION.SDK_INT >= 33) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) { // The device is running API >= 33 and algorithmic darkening is supported.
+ // 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 algorithmic darkening.
+ WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), false);
+ } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected.
+ // Turn on algorithmic darkening.
+ WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), true);
+ } 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 algorithmic darkening according to the current system theme status.
+ WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), currentThemeStatus == Configuration.UI_MODE_NIGHT_YES);
+ }
+ } else if ((Build.VERSION.SDK_INT < 33) && WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { // The device is running API < 33 and the WebView supports 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.
+ //noinspection deprecation
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.
+ //noinspection deprecation
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
} else { // The system default theme is selected.
// Get the current system theme status.
// Set the WebView theme according to the current system theme status.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode.
// Turn off the WebView dark mode.
+ //noinspection deprecation
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
} else { // The system is in night mode.
// Turn on the WebView dark mode.
+ //noinspection deprecation
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
}
}
// Load the URL if directed. This makes sure that the domain settings are properly loaded before the URL. By using `loadUrl()`, instead of `loadUrlFromBase()`, the Referer header will never be sent.
if (loadUrl) {
- nestedScrollWebView.loadUrl(url, customHeaders);
+ nestedScrollWebView.loadUrl(url, nestedScrollWebView.getXRequestedWithHeader());
}
}
}
private String sanitizeUrl(String url) {
- // Sanitize Google Analytics.
- if (sanitizeGoogleAnalytics) {
- // Remove `?utm_`.
- if (url.contains("?utm_")) {
- url = url.substring(0, url.indexOf("?utm_"));
- }
-
- // Remove `&utm_`.
- if (url.contains("&utm_")) {
- url = url.substring(0, url.indexOf("&utm_"));
- }
- }
-
- // 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="));
- }
-
- // Remove `?fbadid=`.
- if (url.contains("?fbadid=")) {
- url = url.substring(0, url.indexOf("?fbadid="));
- }
-
- // Remove `&fbadid=`.
- if (url.contains("&fbadid=")) {
- url = url.substring(0, url.indexOf("&fbadid="));
- }
- }
+ // Sanitize tracking queries.
+ if (sanitizeTrackingQueries)
+ url = sanitizeUrlHelper.sanitizeTrackingQueries(url);
- // Sanitize Twitter AMP redirects.
- if (sanitizeTwitterAmpRedirects) {
- // Remove `?amp=1`.
- if (url.contains("?amp=1")) {
- url = url.substring(0, url.indexOf("?amp=1"));
- }
- }
+ // Sanitize AMP redirects.
+ if (sanitizeAmpRedirects)
+ url = sanitizeUrlHelper.sanitizeAmpRedirects(url);
// Return the sanitized URL.
return url;
// Delete the secondary `Service Worker` cache directory.
// A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
- Process deleteServiceWorkerProcess = runtime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"});
+ Process deleteServiceWorkerProcess = runtime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Default/Service Worker/"});
// Wait until the processes have finished.
deleteCacheProcess.waitFor();
}
}
- // Clear the custom headers.
- customHeaders.clear();
-
// Manually delete the `app_webview` folder, which contains the cookies, DOM storage, form data, and `Service Worker` cache.
// See `https://code.google.com/p/android/issues/detail?id=233826&thanks=233826&ts=1486670530`.
if (clearEverything) {
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)) {
+ if ((Build.VERSION.SDK_INT >= 33) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) { // The device is running API >= 33 and algorithmic darkening is supported.
+ // Set the WebView them. 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 algorithmic darkening.
+ WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), false);
+
+ // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+ // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`.
+ nestedScrollWebView.setVisibility(View.VISIBLE);
+ } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected.
+ // Turn on algorithmic darkening.
+ WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), true);
+ } else {
+ // The system default theme is selected.
+ int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ // Set the algorithmic darkening according to the current system theme status.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode.
+ // Turn off algorithmic darkening.
+ WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), false);
+
+ // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+ // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`.
+ nestedScrollWebView.setVisibility(View.VISIBLE);
+ } else { // The system is in night mode.
+ // Turn on algorithmic darkening.
+ WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), true);
+ }
+ }
+ } else if ((Build.VERSION.SDK_INT < 33) && WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { // The device is running API < 33 and the WebView supports 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.
+ //noinspection deprecation
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
// Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode.
nestedScrollWebView.setVisibility(View.VISIBLE);
} else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected.
// Turn on the WebView dark mode.
+ //noinspection deprecation
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
} else { // The system default theme is selected.
// Get the current system theme status.
// Set the WebView theme according to the current system theme status.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode.
// Turn off the WebView dark mode.
+ //noinspection deprecation
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
// Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode.
nestedScrollWebView.setVisibility(View.VISIBLE);
} else { // The system is in night mode.
// Turn on the WebView dark mode.
+ //noinspection deprecation
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
}
}
// Calculate the Y change.
float motionY = motionEvent2.getY() - motionEvent1.getY();
- // Scroll the app bar if the change is greater than 100 pixels.
+ // Scroll the app bar if the change is greater than 50 pixels.
if (motionY > 50) {
// Animate the bottom app bar onto the screen.
objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", 0);
}
}
+ // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`,
+ // which links to `/data/data/com.stoutner.privacybrowser.standard`.
+ String privateDataDirectoryString = getApplicationInfo().dataDir;
+
// Clear the cache, history, and logcat if Incognito Mode is enabled.
if (incognitoModeEnabled) {
// Clear the cache. `true` includes disk files.
// Manually delete cache folders.
try {
- // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`,
- // which links to `/data/data/com.stoutner.privacybrowser.standard`.
- String privateDataDirectoryString = getApplicationInfo().dataDir;
-
// Delete the main cache directory.
Runtime.getRuntime().exec("rm -rf " + privateDataDirectoryString + "/cache");
-
- // Delete the secondary `Service Worker` cache directory.
- // A `String[]` must be used because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise.
- Runtime.getRuntime().exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"});
} catch (IOException exception) {
// Do nothing if an error is thrown.
}
}
}
+ // Clear the `Service Worker` directory.
+ try {
+ // A `String[]` must be used because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise.
+ Runtime.getRuntime().exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Default/Service Worker/"});
+ } catch (IOException exception) {
+ // Do nothing.
+ }
+
// Get the current page position.
int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());