import android.Manifest;
import android.annotation.SuppressLint;
+import android.app.Activity;
import android.app.DialogFragment;
import android.app.DownloadManager;
import android.content.ActivityNotFoundException;
HttpAuthenticationDialog.HttpAuthenticationListener, NavigationView.OnNavigationItemSelectedListener, PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener,
SslCertificateErrorDialog.SslCertificateErrorListener, UrlHistoryDialog.UrlHistoryListener {
- // `darkTheme` is public static so it can be accessed from `AboutActivity`, `GuideActivity`, `AddDomainDialog`, `SettingsActivity`, `DomainsActivity`, `DomainsListFragment`, `BookmarksActivity`,
- // `BookmarksDatabaseViewActivity`, `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `DownloadFileDialog`, `DownloadImageDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`,
- // `EditBookmarkDatabaseViewDialog`, `HttpAuthenticationDialog`, `MoveToFolderDialog`, `SslCertificateErrorDialog`, `UrlHistoryDialog`, `ViewSslCertificateDialog`, `CreateHomeScreenShortcutDialog`,
- // and `OrbotProxyHelper`. It is also used in `onCreate()`, `applyAppSettings()`, `applyDomainSettings()`, and `updatePrivacyIcons()`.
+ // `darkTheme` is public static so it can be accessed from everywhere.
public static boolean darkTheme;
// `allowScreenshots` is public static so it can be accessed from everywhere. It is also used in `onCreate()`.
public static Bitmap favoriteIconBitmap;
// `formattedUrlString` is public static so it can be accessed from `BookmarksActivity`, `CreateBookmarkDialog`, and `AddDomainDialog`.
- // It is also used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onCreateHomeScreenShortcutCreate()`, and `loadUrlFromTextBox()`.
+ // It is also used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onCreateHomeScreenShortcutCreate()`, `loadUrlFromTextBox()`, and `applyProxyThroughOrbot()`.
public static String formattedUrlString;
// `sslCertificate` is public static so it can be accessed from `DomainsActivity`, `DomainsListFragment`, `DomainSettingsFragment`, `PinnedSslCertificateMismatchDialog`,
// and `ViewSslCertificateDialog`. It is also used in `onCreate()`.
public static SslCertificate sslCertificate;
- // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`.
+ // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`.
public static String orbotStatus;
// `webViewTitle` is public static so it can be accessed from `CreateBookmarkDialog` and `CreateHomeScreenShortcutDialog`. It is also used in `onCreate()`.
// The block list versions are public static so they can be accessed from `AboutTabFragment`. They are also used in `onCreate()`.
public static String easyListVersion;
public static String easyPrivacyVersion;
- public static String fanboyAnnoyanceVersion;
- public static String fanboySocialVersion;
+ public static String fanboysAnnoyanceVersion;
+ public static String fanboysSocialVersion;
+ public static String ultraPrivacyVersion;
+
+ // The request items are public static so they can be accessed by `BlockListHelper`, `RequestsArrayAdapter`, and `ViewRequestsDialog`. They are also used in `onCreate()` and `onPrepareOptionsMenu()`.
+ public static List<String[]> resourceRequests;
+ public static String[] whiteListResultStringArray;
+ private int blockedRequests;
+ private int easyListBlockedRequests;
+ private int easyPrivacyBlockedRequests;
+ private int fanboysAnnoyanceListBlockedRequests;
+ private int fanboysSocialBlockingListBlockedRequests;
+ private int ultraPrivacyBlockedRequests;
+ private int thirdPartyBlockedRequests;
+
+ public final static int REQUEST_DISPOSITION = 0;
+ public final static int REQUEST_URL = 1;
+ public final static int REQUEST_BLOCKLIST = 2;
+ public final static int REQUEST_SUBLIST = 3;
+ public final static int REQUEST_BLOCKLIST_ENTRIES = 4;
+ public final static int REQUEST_BLOCKLIST_ORIGINAL_ENTRY = 5;
+
+ public final static int REQUEST_DEFAULT = 0;
+ public final static int REQUEST_ALLOWED = 1;
+ public final static int REQUEST_THIRD_PARTY = 2;
+ public final static int REQUEST_BLOCKED = 3;
+
+ public final static int MAIN_WHITELIST = 1;
+ public final static int FINAL_WHITELIST = 2;
+ public final static int DOMAIN_WHITELIST = 3;
+ public final static int DOMAIN_INITIAL_WHITELIST = 4;
+ public final static int DOMAIN_FINAL_WHITELIST = 5;
+ public final static int THIRD_PARTY_WHITELIST = 6;
+ public final static int THIRD_PARTY_DOMAIN_WHITELIST = 7;
+ public final static int THIRD_PARTY_DOMAIN_INITIAL_WHITELIST = 8;
+
+ public final static int MAIN_BLACKLIST = 9;
+ public final static int INITIAL_BLACKLIST = 10;
+ public final static int FINAL_BLACKLIST = 11;
+ public final static int DOMAIN_BLACKLIST = 12;
+ public final static int DOMAIN_INITIAL_BLACKLIST = 13;
+ public final static int DOMAIN_FINAL_BLACKLIST = 14;
+ public final static int DOMAIN_REGULAR_EXPRESSION_BLACKLIST = 15;
+ public final static int THIRD_PARTY_BLACKLIST = 16;
+ public final static int THIRD_PARTY_INITIAL_BLACKLIST = 17;
+ public final static int THIRD_PARTY_DOMAIN_BLACKLIST = 18;
+ public final static int THIRD_PARTY_DOMAIN_INITIAL_BLACKLIST = 19;
+ public final static int THIRD_PARTY_REGULAR_EXPRESSION_BLACKLIST = 20;
+ public final static int THIRD_PARTY_DOMAIN_REGULAR_EXPRESSION_BLACKLIST = 21;
+ public final static int REGULAR_EXPRESSION_BLACKLIST = 22;
+
+ // `blockAllThirdPartyRequests` is public static so it can be accessed from `RequestsActivity`.
+ // It is also used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`
+ public static boolean blockAllThirdPartyRequests;
// `currentBookmarksFolder` is public static so it can be accessed from `BookmarksActivity`. It is also used in `onCreate()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`,
// `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
public final static int DOMAINS_CUSTOM_USER_AGENT = 13;
- // `appBar` is used in `onCreate()`, `onOptionsItemSelected()`, `closeFindOnPage()`, and `applyAppSettings()`.
+ // `appBar` is used in `onCreate()`, `onOptionsItemSelected()`, `closeFindOnPage()`, `applyAppSettings()`, and `applyProxyThroughOrbot()`.
private ActionBar appBar;
// `navigatingHistory` is used in `onCreate()`, `onNavigationItemSelected()`, `onSslMismatchBack()`, and `applyDomainSettings()`.
private CoordinatorLayout rootCoordinatorLayout;
// `mainWebView` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`,
- // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, and `setDisplayWebpageImages()`.
+ // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `setDisplayWebpageImages()`, and `applyProxyThroughOrbot()`.
private WebView mainWebView;
// `fullScreenVideoFrameLayout` is used in `onCreate()` and `onConfigurationChanged()`.
private FrameLayout fullScreenVideoFrameLayout;
- // `swipeRefreshLayout` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsMenuSelected()`, and `onRestart()`.
+ // `swipeRefreshLayout` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `onRestart()`.
private SwipeRefreshLayout swipeRefreshLayout;
// `urlAppBarRelativeLayout` is used in `onCreate()` and `applyDomainSettings()`.
// `saveFormDataEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. It can be removed once the minimum API >= 26.
private boolean saveFormDataEnabled;
- // `nightMode` is used in `onCreate()` and `applyDomainSettings()`.
+ // `nightMode` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.
private boolean nightMode;
// `displayWebpageImagesBoolean` is used in `applyAppSettings()` and `applyDomainSettings()`.
private boolean displayWebpageImagesBoolean;
- // 'homepage' is used in `onCreate()`, `onNavigationItemSelected()`, and `applyAppSettings()`.
+ // 'homepage' is used in `onCreate()`, `onNavigationItemSelected()`, and `applyProxyThroughOrbot()`.
private String homepage;
- // `searchURL` is used in `loadURLFromTextBox()` and `applyAppSettings()`.
+ // `searchURL` is used in `loadURLFromTextBox()` and `applyProxyThroughOrbot()`.
private String searchURL;
- // The block list variables are used in `onCreate()` and `applyAppSettings()`.
+ // `mainMenu` is used in `onCreateOptionsMenu()` and `updatePrivacyIcons()`.
+ private Menu mainMenu;
+
+ // `refreshMenuItem` is used in `onCreate()` and `onCreateOptionsMenu()`.
+ private MenuItem refreshMenuItem;
+
+ // The blocklist menu items are used in `onCreate()`, `onCreateOptionsMenu()`, and `onPrepareOptionsMenu()`.
+ private MenuItem blocklistsMenuItem;
+ private MenuItem easyListMenuItem;
+ private MenuItem easyPrivacyMenuItem;
+ private MenuItem fanboysAnnoyanceListMenuItem;
+ private MenuItem fanboysSocialBlockingListMenuItem;
+ private MenuItem ultraPrivacyMenuItem;
+ private MenuItem blockAllThirdPartyRequestsMenuItem;
+
+ // The blocklist variables are used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`.
private boolean easyListEnabled;
private boolean easyPrivacyEnabled;
private boolean fanboysAnnoyanceListEnabled;
private boolean fanboysSocialBlockingListEnabled;
+ private boolean ultraPrivacyEnabled;
+
+ // `webViewDefaultUserAgent` is used in `onCreate()` and `onPrepareOptionsMenu()`.
+ private String webViewDefaultUserAgent;
+
+ // `defaultCustomUserAgentString` is used in `onPrepareOptionsMenu()` and `applyDomainSettings()`.
+ private String defaultCustomUserAgentString;
// `privacyBrowserRuntime` is used in `onCreate()`, `onOptionsItemSelected()`, and `applyAppSettings()`.
private Runtime privacyBrowserRuntime;
- // `proxyThroughOrbot` is used in `onRestart()` and `applyAppSettings()`.
+ // `proxyThroughOrbot` is used in `onRestart()`, `onOptionsItemSelected()`, `applyAppSettings()`, and `applyProxyThroughOrbot()`.
private boolean proxyThroughOrbot;
// `incognitoModeEnabled` is used in `onCreate()` and `applyAppSettings()`.
// `reapplyAppSettingsOnRestart` is used in `onNavigationItemSelected()` and `onRestart()`.
private boolean reapplyAppSettingsOnRestart;
+ // `displayingFullScreenVideo` is used in `onCreate()` and `onResume()`.
+ private boolean displayingFullScreenVideo;
+
// `currentDomainName` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onAddDomain()`, and `applyDomainSettings()`.
private String currentDomainName;
// `ignorePinnedSslCertificateForDomain` is used in `onCreate()`, `onSslMismatchProceed()`, and `applyDomainSettings()`.
private boolean ignorePinnedSslCertificate;
- // `waitingForOrbot` is used in `onCreate()` and `applyAppSettings()`.
+ // `orbotStatusBroadcastReceiver` is used in `onCreate()` and `onDestroy()`.
+ private BroadcastReceiver orbotStatusBroadcastReceiver;
+
+ // `waitingForOrbot` is used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`.
private boolean waitingForOrbot;
// `domainSettingsApplied` is used in `prepareOptionsMenu()`, `applyDomainSettings()`, and `setDisplayWebpageImages()`.
private boolean domainSettingsApplied;
+ // `domainSettingsJavaScriptEnabled` is used in `onOptionsItemSelected()` and `applyDomainSettings()`.
+ private Boolean domainSettingsJavaScriptEnabled;
+
// `displayWebpageImagesInt` is used in `applyDomainSettings()` and `setDisplayWebpageImages()`.
private int displayWebpageImagesInt;
// `onTheFlyDisplayImagesSet` is used in `applyDomainSettings()` and `setDisplayWebpageImages()`.
private boolean onTheFlyDisplayImagesSet;
- // `waitingForOrbotData` is used in `onCreate()` and `applyAppSettings()`.
- private String waitingForOrbotHTMLString;
+ // `waitingForOrbotHtmlString` is used in `onCreate()` and `applyProxyThroughOrbot()`.
+ private String waitingForOrbotHtmlString;
// `privateDataDirectoryString` is used in `onCreate()`, `onOptionsItemSelected()`, and `onNavigationItemSelected()`.
private String privateDataDirectoryString;
// `findOnPageEditText` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`.
private EditText findOnPageEditText;
- // `mainMenu` is used in `onCreateOptionsMenu()` and `updatePrivacyIcons()`.
- private Menu mainMenu;
+ // `displayAdditionalAppBarIcons` is used in `onCreate()` and `onCreateOptionsMenu()`.
+ private boolean displayAdditionalAppBarIcons;
// `drawerToggle` is used in `onCreate()`, `onPostCreate()`, `onConfigurationChanged()`, `onNewIntent()`, and `onNavigationItemSelected()`.
private ActionBarDrawerToggle drawerToggle;
// `mainWebViewRelativeLayout` is used in `onCreate()` and `onNavigationItemSelected()`.
private RelativeLayout mainWebViewRelativeLayout;
- // `urlIsLoading` is used in `onCreate()`, `loadUrl()`, and `applyDomainSettings()`.
+ // `urlIsLoading` is used in `onCreate()`, `onCreateOptionsMenu()`, `loadUrl()`, and `applyDomainSettings()`.
private boolean urlIsLoading;
// `pinnedDomainSslCertificate` is used in `onCreate()` and `applyDomainSettings()`.
private boolean pinnedDomainSslCertificate;
- // `bookmarksDatabaseHelper` is used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
+ // `bookmarksDatabaseHelper` is used in `onCreate()`, `onDestroy`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`,
+ // and `loadBookmarksFolder()`.
private BookmarksDatabaseHelper bookmarksDatabaseHelper;
// `bookmarksListView` is used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, and `loadBookmarksFolder()`.
// `bookmarksTitleTextView` is used in `onCreate()` and `loadBookmarksFolder()`.
private TextView bookmarksTitleTextView;
- // `bookmarksCursor` is used in `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
+ // `bookmarksCursor` is used in `onDestroy()`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
private Cursor bookmarksCursor;
// `bookmarksCursorAdapter` is used in `onCreateBookmark()`, `onCreateBookmarkFolder()` `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
// Remove the formatting from `urlTextBar` when the user is editing the text.
urlTextBox.setOnFocusChangeListener((View v, boolean hasFocus) -> {
- if (hasFocus) { // The user is editing `urlTextBox`.
+ if (hasFocus) { // The user is editing the URL text box.
// Remove the highlighting.
urlTextBox.getText().removeSpan(redColorSpan);
urlTextBox.getText().removeSpan(initialGrayColorSpan);
urlTextBox.getText().removeSpan(finalGrayColorSpan);
- } else { // The user has stopped editing `urlTextBox`.
+ } else { // The user has stopped editing the URL text box.
+ // Move to the beginning of the string.
+ urlTextBox.setSelection(0);
+
// Reapply the highlighting.
highlightUrlText();
}
});
// Set `waitingForOrbotHTMLString`.
- waitingForOrbotHTMLString = "<html><body><br/><center><h1>" + getString(R.string.waiting_for_orbot) + "</h1></center></body></html>";
+ waitingForOrbotHtmlString = "<html><body><br/><center><h1>" + getString(R.string.waiting_for_orbot) + "</h1></center></body></html>";
// Initialize `currentDomainName`, `orbotStatus`, and `waitingForOrbot`.
currentDomainName = "";
waitingForOrbot = false;
// Create an Orbot status `BroadcastReceiver`.
- BroadcastReceiver orbotStatusBroadcastReceiver = new BroadcastReceiver() {
+ orbotStatusBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Store the content of the status message in `orbotStatus`.
orbotStatus = intent.getStringExtra("org.torproject.android.intent.extra.STATUS");
- // If we are waiting on Orbot, load the website now that Orbot is connected.
+ // If Privacy Browser is waiting on Orbot, load the website now that Orbot is connected.
if (orbotStatus.equals("ON") && waitingForOrbot) {
// Reset `waitingForOrbot`.
waitingForOrbot = false;
// Show the `BannerAd` in the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
// Reload the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
- AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_id));
+ AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_unit_id));
}
// Remove the translucent navigation bar flag if it is set.
final MenuItem navigationBackMenuItem = navigationMenu.getItem(1);
final MenuItem navigationForwardMenuItem = navigationMenu.getItem(2);
final MenuItem navigationHistoryMenuItem = navigationMenu.getItem(3);
+ final MenuItem navigationRequestsMenuItem = navigationMenu.getItem(4);
// Initialize the bookmarks database helper. `this` specifies the context. The two `nulls` do not specify the database name or a `CursorFactory`.
// The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
return true;
});
- // The `DrawerListener` allows us to update the Navigation Menu.
+ // The drawer listener is used to update the navigation menu.
drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
@Override
public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
@Override
public void onDrawerStateChanged(int newState) {
- if ((newState == DrawerLayout.STATE_SETTLING) || (newState == DrawerLayout.STATE_DRAGGING)) { // The drawer is opening or closing.
- // Update the `Back`, `Forward`, and `History` menu items.
+ if ((newState == DrawerLayout.STATE_SETTLING) || (newState == DrawerLayout.STATE_DRAGGING)) { // A drawer is opening or closing.
+ // Update the back, forward, history, and requests menu items.
navigationBackMenuItem.setEnabled(mainWebView.canGoBack());
navigationForwardMenuItem.setEnabled(mainWebView.canGoForward());
navigationHistoryMenuItem.setEnabled((mainWebView.canGoBack() || mainWebView.canGoForward()));
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
- // Hide the keyboard (if displayed) so we can see the navigation menu. `0` indicates no additional flags.
+ // Hide the keyboard (if displayed).
inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
- // Clear the focus from `urlTextBox` if it has it.
+ // Clear the focus from from the URL text box.
urlTextBox.clearFocus();
}
}
mainWebView.evaluateJavascript("(function() {var parent = document.getElementsByTagName('head').item(0); var style = document.createElement('style'); style.type = 'text/css'; " +
"style.innerHTML = '* {background-color: #212121 !important; color: #BDBDBD !important; box-shadow: none !important; text-decoration: none !important;" +
"text-shadow: none !important; border: none !important;} a {color: #1565C0 !important;}'; parent.appendChild(style)})()", value -> {
- // Initialize a `Handler` to display `mainWebView`.
+ // 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.
+ // Setup a runnable to display `mainWebView` after a delay to allow the CSS to be applied.
Runnable displayWebViewRunnable = () -> {
// Only display `mainWebView` if the progress bar is one. This prevents the display of the `WebView` while it is still loading.
if (progressBar.getVisibility() == View.GONE) {
}
};
- // Use `displayWebViewHandler` to delay the displaying of `mainWebView` for 500 milliseconds.
+ // Displaying of `mainWebView` after 500 milliseconds.
displayWebViewHandler.postDelayed(displayWebViewRunnable, 500);
});
}
// Enter full screen video.
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
+ // Set the full screen video flag.
+ displayingFullScreenVideo = true;
+
// Pause the ad if this is the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
// The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
// Set `rootCoordinatorLayout` to fill the entire screen.
rootCoordinatorLayout.setFitsSystemWindows(false);
+ // Disable the sliding drawers.
+ drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
+
// Add `view` to `fullScreenVideoFrameLayout` and display it on the screen.
fullScreenVideoFrameLayout.addView(view);
fullScreenVideoFrameLayout.setVisibility(View.VISIBLE);
// Exit full screen video.
@Override
public void onHideCustomView() {
+ // Unset the full screen video flag.
+ displayingFullScreenVideo = false;
+
// Hide `fullScreenVideoFrameLayout`.
fullScreenVideoFrameLayout.removeAllViews();
fullScreenVideoFrameLayout.setVisibility(View.GONE);
- // Add the translucent status flag. This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`.
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+ // Enable the sliding drawers.
+ drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
+
+ // Apply the appropriate full screen mode the `SYSTEM_UI` flags.
+ if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode.
+ if (hideSystemBarsOnFullscreen) { // Hide everything.
+ // Remove the translucent navigation setting if it is currently flagged.
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+
+ // Remove the translucent status bar overlay.
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+
+ // Remove the translucent status bar overlay on the `Drawer Layout`, which is special and needs its own command.
+ drawerLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+
+ /* SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
+ * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
+ * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
+ */
+ rootCoordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+ } else { // Hide everything except the status and navigation bars.
+ // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`.
+ rootCoordinatorLayout.setSystemUiVisibility(0);
+
+ // Add the translucent status flag if it is unset.
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+
+ if (translucentNavigationBarOnFullscreen) {
+ // Set the navigation bar to be translucent. This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`.
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+ } else {
+ // Set the navigation bar to be black.
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+ }
+ }
+ } else { // Switch to normal viewing mode.
+ // Show the `appBar` if `findOnPageLinearLayout` is not visible.
+ if (findOnPageLinearLayout.getVisibility() == View.GONE) {
+ appBar.show();
+ }
+
+ // Show the `BannerAd` in the free flavor.
+ if (BuildConfig.FLAVOR.contentEquals("free")) {
+ // Initialize the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+ AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id));
+ }
+
+ // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`.
+ rootCoordinatorLayout.setSystemUiVisibility(0);
- // Set `rootCoordinatorLayout` to fit inside the status and navigation bars. This also clears the `SYSTEM_UI` flags.
- rootCoordinatorLayout.setFitsSystemWindows(true);
+ // Remove the translucent navigation bar flag if it is set.
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+
+ // Add the translucent status flag if it is unset. This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`.
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+
+ // Constrain `rootCoordinatorLayout` inside the status and navigation bars.
+ rootCoordinatorLayout.setFitsSystemWindows(true);
+ }
// Show the ad if this is the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
// Reload the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
- AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_id));
+ AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_unit_id));
}
}
// Show a dialog if the user has previously denied the permission.
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first.
- // Get a handle for the download location permission alert dialog and set the download type to DOWNLOAD_FILE.
+ // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_FILE.
DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_FILE);
// Show the download location permission alert dialog. The permission will be requested when the the dialog is closed.
// Request the permission. The download dialog will be launched by `onRequestPermissionResult()`.
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE);
}
- } else { // The WRITE_EXTERNAL_STORAGE permission has already been granted.
+ } else { // The storage permission has already been granted.
// Get a handle for the download file alert dialog.
AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength);
// Replace the header that `WebView` creates for `X-Requested-With` with a null value. The default value is the application ID (com.stoutner.privacybrowser.standard).
customHeaders.put("X-Requested-With", "");
- // Initialize the default preference values the first time the program is run. `this` is the context. `false` keeps this command from resetting any current preferences back to default.
+ // Initialize the default preference values the first time the program is run. `false` keeps this command from resetting any current preferences back to default.
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
// Get the intent that started the app.
saveFormDataEnabled = false; // Form data can be removed once the minimum API >= 26.
nightMode = false;
+ // Store the default user agent.
+ webViewDefaultUserAgent = mainWebView.getSettings().getUserAgentString();
+
// Initialize the WebView title.
webViewTitle = getString(R.string.no_title);
}
// Initialize the user agent array adapter and string array.
- userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.domain_settings_spinner_item);
+ userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item);
userAgentDataArray = getResources().getStringArray(R.array.user_agent_data);
// Apply the app settings from the shared preferences.
// Instantiate the block list helper.
BlockListHelper blockListHelper = new BlockListHelper();
+ // Initialize the list of resource requests.
+ resourceRequests = new ArrayList<>();
+
// Parse the block lists.
final ArrayList<List<String[]>> easyList = blockListHelper.parseBlockList(getAssets(), "blocklists/easylist.txt");
final ArrayList<List<String[]>> easyPrivacy = blockListHelper.parseBlockList(getAssets(), "blocklists/easyprivacy.txt");
- final ArrayList<List<String[]>> fanboyAnnoyance = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-annoyance.txt");
- final ArrayList<List<String[]>> fanboySocial = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-social.txt");
+ final ArrayList<List<String[]>> fanboysAnnoyanceList = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-annoyance.txt");
+ final ArrayList<List<String[]>> fanboysSocialList = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-social.txt");
+ final ArrayList<List<String[]>> ultraPrivacy = blockListHelper.parseBlockList(getAssets(), "blocklists/ultraprivacy.txt");
// Store the list versions.
easyListVersion = easyList.get(0).get(0)[0];
easyPrivacyVersion = easyPrivacy.get(0).get(0)[0];
- fanboyAnnoyanceVersion = fanboyAnnoyance.get(0).get(0)[0];
- fanboySocialVersion = fanboySocial.get(0).get(0)[0];
+ fanboysAnnoyanceVersion = fanboysAnnoyanceList.get(0).get(0)[0];
+ fanboysSocialVersion = fanboysSocialList.get(0).get(0)[0];
+ ultraPrivacyVersion = ultraPrivacy.get(0).get(0)[0];
+
+ // Get a handle for the activity. This is used to update the requests counter while the navigation menu is open.
+ Activity activity = this;
mainWebView.setWebViewClient(new WebViewClient() {
// `shouldOverrideUrlLoading` makes this `WebView` the default handler for URLs inside the app, so that links are not kicked out to other apps.
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("http")) { // Load the URL in Privacy Browser.
- // Hide the WebView while applying domain settings so changes to things like JavaScript aren't rendered on the current URL, which otherwise will reload automatically.
- mainWebView.setVisibility(View.INVISIBLE);
+ // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+ formattedUrlString = "";
- // Apply the domain settings for the new URL.
+ // Apply the domain settings for the new URL. `applyDomainSettings` doesn't do anything if the domain has not changed.
applyDomainSettings(url, true, false);
- // Display the WebView again so that the new URL can be loaded.
- mainWebView.setVisibility(View.VISIBLE);
-
// Returning false causes the current WebView to handle the URL and prevents it from adding redirects to the history list.
return false;
} else if (url.startsWith("mailto:")) { // Load the email address in an external email program.
}
}
- // Check requests against the block lists. The deprecated `shouldInterceptRequest` must be used until minimum API >= 21.
+ // Check requests against the block lists. The deprecated `shouldInterceptRequest()` must be used until minimum API >= 21.
@SuppressWarnings("deprecation")
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url){
// Create an empty web resource response to be used if the resource request is blocked.
WebResourceResponse emptyWebResourceResponse = new WebResourceResponse("text/plain", "utf8", new ByteArrayInputStream("".getBytes()));
+ // Reset the whitelist results tracker.
+ whiteListResultStringArray = null;
+
+ // Initialize the third party request tracker.
+ boolean isThirdPartyRequest = false;
+
+ // Initialize the current domain string.
+ String currentDomain = "";
+
+ // Nobody is happy when comparing null strings.
+ if (!(formattedUrlString == null) && !(url == null)) {
+ // Get the domain strings to URIs.
+ Uri currentDomainUri = Uri.parse(formattedUrlString);
+ Uri requestDomainUri = Uri.parse(url);
+
+ // Get the domain host names.
+ String currentBaseDomain = currentDomainUri.getHost();
+ String requestBaseDomain = requestDomainUri.getHost();
+
+ // Update the current domain variable.
+ currentDomain = currentBaseDomain;
+
+ // Only compare the current base domain and the request base domain if neither is null.
+ if (!(currentBaseDomain == null) && !(requestBaseDomain == null)) {
+ // Determine the current base domain.
+ while (currentBaseDomain.indexOf(".", currentBaseDomain.indexOf(".") + 1) > 0) { // There is at least one subdomain.
+ // Remove the first subdomain.
+ currentBaseDomain = currentBaseDomain.substring(currentBaseDomain.indexOf(".") + 1);
+ }
+
+ // Determine the request base domain.
+ while (requestBaseDomain.indexOf(".", requestBaseDomain.indexOf(".") + 1) > 0) { // There is at least one subdomain.
+ // Remove the first subdomain.
+ requestBaseDomain = requestBaseDomain.substring(requestBaseDomain.indexOf(".") + 1);
+ }
+
+ // Update the third party request tracker.
+ isThirdPartyRequest = !currentBaseDomain.equals(requestBaseDomain);
+ }
+ }
+
+ // Block third-party requests if enabled.
+ if (isThirdPartyRequest && blockAllThirdPartyRequests) {
+ // Increment the blocked requests counters.
+ blockedRequests++;
+ thirdPartyBlockedRequests++;
+
+ // Update the titles of the blocklist menu items. This must be run from the UI thread.
+ activity.runOnUiThread(() -> {
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+ blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+ blockAllThirdPartyRequestsMenuItem.setTitle(thirdPartyBlockedRequests + " - " + getString(R.string.block_all_third_party_requests));
+ });
+
+ // Add the request to the log.
+ resourceRequests.add(new String[]{String.valueOf(REQUEST_THIRD_PARTY), url});
+
+ // Return an empty web resource response.
+ return emptyWebResourceResponse;
+ }
+
+ // Check UltraPrivacy if it is enabled.
+ if (ultraPrivacyEnabled) {
+ if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, ultraPrivacy)) {
+ // Increment the blocked requests counters.
+ blockedRequests++;
+ ultraPrivacyBlockedRequests++;
+
+ // Update the titles of the blocklist menu items. This must be run from the UI thread.
+ activity.runOnUiThread(() -> {
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+ blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+ ultraPrivacyMenuItem.setTitle(ultraPrivacyBlockedRequests + " - " + getString(R.string.ultraprivacy));
+ });
+
+ // The resource request was blocked. Return an empty web resource response.
+ return emptyWebResourceResponse;
+ }
+
+ // If the whitelist result is not null, the request has been allowed by UltraPrivacy.
+ if (whiteListResultStringArray != null) {
+ // Add a whitelist entry to the resource requests array.
+ resourceRequests.add(whiteListResultStringArray);
+
+ // The resource request has been allowed by UltraPrivacy. `return null` loads the requested resource.
+ return null;
+ }
+ }
+
// Check EasyList if it is enabled.
if (easyListEnabled) {
- if (blockListHelper.isBlocked(formattedUrlString, url, easyList)) {
+ if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, easyList)) {
+ // Increment the blocked requests counters.
+ blockedRequests++;
+ easyListBlockedRequests++;
+
+ // Update the titles of the blocklist menu items. This must be run from the UI thread.
+ activity.runOnUiThread(() -> {
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+ blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+ easyListMenuItem.setTitle(easyListBlockedRequests + " - " + getString(R.string.easylist));
+ });
+
+ // Reset the whitelist results tracker (because otherwise it will sometimes add results to the list due to a race condition).
+ whiteListResultStringArray = null;
+
// The resource request was blocked. Return an empty web resource response.
return emptyWebResourceResponse;
}
// Check EasyPrivacy if it is enabled.
if (easyPrivacyEnabled) {
- if (blockListHelper.isBlocked(formattedUrlString, url, easyPrivacy)) {
+ if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, easyPrivacy)) {
+ // Increment the blocked requests counters.
+ blockedRequests++;
+ easyPrivacyBlockedRequests++;
+
+ // Update the titles of the blocklist menu items. This must be run from the UI thread.
+ activity.runOnUiThread(() -> {
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+ blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+ easyPrivacyMenuItem.setTitle(easyPrivacyBlockedRequests + " - " + getString(R.string.easyprivacy));
+ });
+
+ // Reset the whitelist results tracker (because otherwise it will sometimes add results to the list due to a race condition).
+ whiteListResultStringArray = null;
+
// The resource request was blocked. Return an empty web resource response.
return emptyWebResourceResponse;
}
// Check Fanboy’s Annoyance List if it is enabled.
if (fanboysAnnoyanceListEnabled) {
- if (blockListHelper.isBlocked(formattedUrlString, url, fanboyAnnoyance)) {
+ if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, fanboysAnnoyanceList)) {
+ // Increment the blocked requests counters.
+ blockedRequests++;
+ fanboysAnnoyanceListBlockedRequests++;
+
+ // Update the titles of the blocklist menu items. This must be run from the UI thread.
+ activity.runOnUiThread(() -> {
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+ blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+ fanboysAnnoyanceListMenuItem.setTitle(fanboysAnnoyanceListBlockedRequests + " - " + getString(R.string.fanboys_annoyance_list));
+ });
+
+ // Reset the whitelist results tracker (because otherwise it will sometimes add results to the list due to a race condition).
+ whiteListResultStringArray = null;
+
// The resource request was blocked. Return an empty web resource response.
return emptyWebResourceResponse;
}
} else if (fanboysSocialBlockingListEnabled){ // Only check Fanboy’s Social Blocking List if Fanboy’s Annoyance List is disabled.
- if (blockListHelper.isBlocked(formattedUrlString, url, fanboySocial)) {
+ if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, fanboysSocialList)) {
+ // Increment the blocked requests counters.
+ blockedRequests++;
+ fanboysSocialBlockingListBlockedRequests++;
+
+ // Update the titles of the blocklist menu items. This must be run from the UI thread.
+ activity.runOnUiThread(() -> {
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+ blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+ fanboysSocialBlockingListMenuItem.setTitle(fanboysSocialBlockingListBlockedRequests + " - " + getString(R.string.fanboys_social_blocking_list));
+ });
+
+ // Reset the whitelist results tracker (because otherwise it will sometimes add results to the list due to a race condition).
+ whiteListResultStringArray = null;
+
// The resource request was blocked. Return an empty web resource response.
return emptyWebResourceResponse;
}
}
+ // Add the request to the log because it hasn't been processed by any of the previous checks.
+ if (whiteListResultStringArray != null ) { // The request was processed by a whitelist.
+ resourceRequests.add(whiteListResultStringArray);
+ } else { // The request didn't match any blocklist entry. Log it as a default request.
+ resourceRequests.add(new String[]{String.valueOf(REQUEST_DEFAULT), url});
+ }
+
// The resource request has not been blocked. `return null` loads the requested resource.
return null;
}
// Update the URL in urlTextBox when the page starts to load.
@Override
- public void onPageStarted(WebView view, String url, Bitmap favicon) {// If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ // Reset the list of resource requests.
+ resourceRequests.clear();
+
+ // Initialize the counters for requests blocked by each blocklist.
+ blockedRequests = 0;
+ easyListBlockedRequests = 0;
+ easyPrivacyBlockedRequests = 0;
+ fanboysAnnoyanceListBlockedRequests = 0;
+ fanboysSocialBlockingListBlockedRequests = 0;
+ ultraPrivacyBlockedRequests = 0;
+ thirdPartyBlockedRequests = 0;
+
+ // If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
if (nightMode) {
mainWebView.setVisibility(View.INVISIBLE);
}
// Hide the keyboard. `0` indicates no additional flags.
inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
- // Check to see if we are waiting on Orbot.
+ // Check to see if Privacy Browser is waiting on Orbot.
if (!waitingForOrbot) { // We are not waiting on Orbot, so we need to process the URL.
// We need to update `formattedUrlString` at the beginning of the load, so that if the user toggles JavaScript during the load the new website is reloaded.
formattedUrlString = url;
// Apply any custom domain settings if the URL was loaded by navigating history.
if (navigatingHistory) {
+ // Reset `navigatingHistory`.
+ navigatingHistory = false;
+
+ // Apply the domain settings.
applyDomainSettings(url, true, false);
}
// Set `urlIsLoading` to `true`, so that redirects while loading do not trigger changes in the user agent, which forces another reload of the existing page.
urlIsLoading = true;
+
+ // Replace Refresh with Stop if the menu item has been created. (The WebView typically begins loading before the menu items are instantiated.)
+ if (refreshMenuItem != null) {
+ // Set the title.
+ refreshMenuItem.setTitle(R.string.stop);
+
+ // If the icon is displayed in the AppBar, set it according to the theme.
+ if (displayAdditionalAppBarIcons) {
+ if (darkTheme) {
+ refreshMenuItem.setIcon(R.drawable.close_dark);
+ } else {
+ refreshMenuItem.setIcon(R.drawable.close_light);
+ }
+ }
+ }
}
}
// It is necessary to update `formattedUrlString` and `urlTextBox` after the page finishes loading because the final URL can change during load.
@Override
public void onPageFinished(WebView view, String url) {
+ // Reset the wide view port if it has been turned off by the waiting for Orbot message.
+ if (!waitingForOrbot) {
+ mainWebView.getSettings().setUseWideViewPort(true);
+ }
+
// Flush any cookies to persistent storage. `CookieManager` has become very lazy about flushing cookies in recent versions.
if (firstPartyCookiesEnabled && Build.VERSION.SDK_INT >= 21) {
cookieManager.flush();
}
+ // Update the Refresh menu item if it has been created.
+ if (refreshMenuItem != null) {
+ // Reset the Refresh title.
+ refreshMenuItem.setTitle(R.string.refresh);
+
+ // If the icon is displayed in the AppBar, reset it according to the theme.
+ if (displayAdditionalAppBarIcons) {
+ if (darkTheme) {
+ refreshMenuItem.setIcon(R.drawable.refresh_enabled_dark);
+ } else {
+ refreshMenuItem.setIcon(R.drawable.refresh_enabled_light);
+ }
+ }
+ }
+
// Reset `urlIsLoading`, which is used to prevent reloads on redirect if the user agent changes.
urlIsLoading = false;
// Check to see if the intent contains a new URL.
if (intent.getData() != null) {
- // Get the intent data and convert it to a string.
+ // Get the intent data.
final Uri intentUriData = intent.getData();
- formattedUrlString = intentUriData.toString();
// Load the website.
- loadUrl(formattedUrlString);
+ loadUrl(intentUriData.toString());
// Close the navigation drawer if it is open.
if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
// Resume the adView for the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
- // The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+ // Resume the ad.
AdHelper.resumeAd(findViewById(R.id.adview));
}
+
+ // Display a message to the user if waiting for Orbot.
+ if (waitingForOrbot && !orbotStatus.equals("ON")) {
+ // Disable the wide view port so that the waiting for Orbot text is displayed correctly.
+ mainWebView.getSettings().setUseWideViewPort(false);
+
+ // Load a waiting page. `null` specifies no encoding, which defaults to ASCII.
+ mainWebView.loadData(waitingForOrbotHtmlString, "text/html", null);
+ }
+
+ if (displayingFullScreenVideo) {
+ // Remove the translucent overlays.
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+
+ // Remove the translucent status bar overlay on the `Drawer Layout`, which is special and needs its own command.
+ drawerLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+
+ /* SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
+ * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
+ * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
+ */
+ rootCoordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+ }
}
@Override
public void onPause() {
+ // Run the default commands.
+ super.onPause();
+
// Pause `mainWebView`.
mainWebView.onPause();
// Stop all JavaScript.
mainWebView.pauseTimers();
- // Pause the adView or it will continue to consume resources in the background on the free flavor.
+ // Pause the ad or it will continue to consume resources in the background on the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
- // The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+ // Pause the ad.
AdHelper.pauseAd(findViewById(R.id.adview));
}
+ }
- super.onPause();
+ @Override
+ public void onDestroy() {
+ // Unregister the Orbot status broadcast receiver.
+ this.unregisterReceiver(orbotStatusBroadcastReceiver);
+
+ // Close the bookmarks cursor and database.
+ bookmarksCursor.close();
+ bookmarksDatabaseHelper.close();
+
+ // Run the default commands.
+ super.onDestroy();
}
@Override
MenuItem toggleDomStorageMenuItem = menu.findItem(R.id.toggle_dom_storage);
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);
+ refreshMenuItem = menu.findItem(R.id.refresh);
+ blocklistsMenuItem = menu.findItem(R.id.blocklists);
+ easyListMenuItem = menu.findItem(R.id.easylist);
+ easyPrivacyMenuItem = menu.findItem(R.id.easyprivacy);
+ fanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list);
+ fanboysSocialBlockingListMenuItem = menu.findItem(R.id.fanboys_social_blocking_list);
+ ultraPrivacyMenuItem = menu.findItem(R.id.ultraprivacy);
+ blockAllThirdPartyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests);
MenuItem adConsentMenuItem = menu.findItem(R.id.ad_consent);
// Only display third-party cookies if API >= 21
// Only show Ad Consent if this is the free flavor.
adConsentMenuItem.setVisible(BuildConfig.FLAVOR.contentEquals("free"));
- // Get the shared preference values. `this` references the current context.
+ // Get the shared preference values.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ // Get the status of the additional AppBar icons.
+ displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+
// Set the status of the additional app bar icons. Setting the refresh menu item to `SHOW_AS_ACTION_ALWAYS` makes it appear even on small devices like phones.
- if (sharedPreferences.getBoolean("display_additional_app_bar_icons", false)) {
+ if (displayAdditionalAppBarIcons) {
toggleFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
toggleDomStorageMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
}
+ // Replace Refresh with Stop if a URL is already loading.
+ if (urlIsLoading) {
+ // Set the title.
+ refreshMenuItem.setTitle(R.string.stop);
+
+ // If the icon is displayed in the AppBar, set it according to the theme.
+ if (displayAdditionalAppBarIcons) {
+ if (darkTheme) {
+ refreshMenuItem.setIcon(R.drawable.close_dark);
+ } else {
+ refreshMenuItem.setIcon(R.drawable.close_light);
+ }
+ }
+ }
+
return true;
}
MenuItem fontSizeMenuItem = menu.findItem(R.id.font_size);
MenuItem swipeToRefreshMenuItem = menu.findItem(R.id.swipe_to_refresh);
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);
// Set the text for the domain menu item.
if (domainSettingsApplied) {
toggleThirdPartyCookiesMenuItem.setChecked(thirdPartyCookiesEnabled);
toggleDomStorageMenuItem.setChecked(domStorageEnabled);
toggleSaveFormDataMenuItem.setChecked(saveFormDataEnabled); // Form data can be removed once the minimum API >= 26.
+ easyListMenuItem.setChecked(easyListEnabled);
+ easyPrivacyMenuItem.setChecked(easyPrivacyEnabled);
+ fanboysAnnoyanceListMenuItem.setChecked(fanboysAnnoyanceListEnabled);
+ fanboysSocialBlockingListMenuItem.setChecked(fanboysSocialBlockingListEnabled);
+ ultraPrivacyMenuItem.setChecked(ultraPrivacyEnabled);
+ blockAllThirdPartyRequestsMenuItem.setChecked(blockAllThirdPartyRequests);
swipeToRefreshMenuItem.setChecked(swipeRefreshLayout.isEnabled());
displayImagesMenuItem.setChecked(mainWebView.getSettings().getLoadsImagesAutomatically());
+ nightModeMenuItem.setChecked(nightMode);
+ proxyThroughOrbotMenuItem.setChecked(proxyThroughOrbot);
// Enable third-party cookies if first-party cookies are enabled.
toggleThirdPartyCookiesMenuItem.setEnabled(firstPartyCookiesEnabled);
- // Enable `DOM Storage` if JavaScript is enabled.
+ // Enable DOM Storage if JavaScript is enabled.
toggleDomStorageMenuItem.setEnabled(javaScriptEnabled);
- // Enable `Clear Cookies` if there are any.
+ // Enable Clear Cookies if there are any.
clearCookiesMenuItem.setEnabled(cookieManager.hasCookies());
- // Get a count of the number of files in the `Local Storage` directory.
+ // Get a count of the number of files in the Local Storage directory.
File localStorageDirectory = new File (privateDataDirectoryString + "/app_webview/Local Storage/");
int localStorageDirectoryNumberOfFiles = 0;
if (localStorageDirectory.exists()) {
localStorageDirectoryNumberOfFiles = localStorageDirectory.list().length;
}
- // Get a count of the number of files in the `IndexedDB` directory.
+ // Get a count of the number of files in the IndexedDB directory.
File indexedDBDirectory = new File (privateDataDirectoryString + "/app_webview/IndexedDB");
int indexedDBDirectoryNumberOfFiles = 0;
if (indexedDBDirectory.exists()) {
indexedDBDirectoryNumberOfFiles = indexedDBDirectory.list().length;
}
- // Enable `Clear DOM Storage` if there is any.
+ // Enable Clear DOM Storage if there is any.
clearDOMStorageMenuItem.setEnabled(localStorageDirectoryNumberOfFiles > 0 || indexedDBDirectoryNumberOfFiles > 0);
- // Enable `Clear Form Data` is there is any. This can be removed once the minimum API >= 26.
+ // Enable Clear Form Data is there is any. This can be removed once the minimum API >= 26.
if (Build.VERSION.SDK_INT < 26) {
WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this);
clearFormDataMenuItem.setEnabled(mainWebViewDatabase.hasFormData());
+ } else {
+ // Disable clear form data because it is not supported on current version of Android.
+ clearFormDataMenuItem.setEnabled(false);
}
- // Enable `Clear Data` if any of the submenu items are enabled.
+ // Enable Clear Data if any of the submenu items are enabled.
clearDataMenuItem.setEnabled(clearCookiesMenuItem.isEnabled() || clearDOMStorageMenuItem.isEnabled() || clearFormDataMenuItem.isEnabled());
+ // Disable Fanboy's Social Blocking List if Fanboy's Annoyance List is checked.
+ fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListEnabled);
+
+ // Initialize the display names for the blocklists with the number of blocked requests.
+ blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + blockedRequests);
+ easyListMenuItem.setTitle(easyListBlockedRequests + " - " + getString(R.string.easylist));
+ easyPrivacyMenuItem.setTitle(easyPrivacyBlockedRequests + " - " + getString(R.string.easyprivacy));
+ fanboysAnnoyanceListMenuItem.setTitle(fanboysAnnoyanceListBlockedRequests + " - " + getString(R.string.fanboys_annoyance_list));
+ fanboysSocialBlockingListMenuItem.setTitle(fanboysSocialBlockingListBlockedRequests + " - " + getString(R.string.fanboys_social_blocking_list));
+ ultraPrivacyMenuItem.setTitle(ultraPrivacyBlockedRequests + " - " + getString(R.string.ultraprivacy));
+ blockAllThirdPartyRequestsMenuItem.setTitle(thirdPartyBlockedRequests + " - " + getString(R.string.block_all_third_party_requests));
+
+ // Get the current user agent.
+ String currentUserAgent = mainWebView.getSettings().getUserAgentString();
+
+ // Select the current user agent menu item. A switch statement cannot be used because the user agents are not compile time constants.
+ if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[0])) { // Privacy Browser.
+ menu.findItem(R.id.user_agent_privacy_browser).setChecked(true);
+ } else if (currentUserAgent.equals(webViewDefaultUserAgent)) { // WebView Default.
+ menu.findItem(R.id.user_agent_webview_default).setChecked(true);
+ } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[2])) { // Firefox on Android.
+ menu.findItem(R.id.user_agent_firefox_on_android).setChecked(true);
+ } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[3])) { // Chrome on Android.
+ menu.findItem(R.id.user_agent_chrome_on_android).setChecked(true);
+ } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[4])) { // Safari on iOS.
+ menu.findItem(R.id.user_agent_safari_on_ios).setChecked(true);
+ } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[5])) { // Firefox on Linux.
+ menu.findItem(R.id.user_agent_firefox_on_linux).setChecked(true);
+ } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[6])) { // Chromium on Linux.
+ menu.findItem(R.id.user_agent_chromium_on_linux).setChecked(true);
+ } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[7])) { // Firefox on Windows.
+ menu.findItem(R.id.user_agent_firefox_on_windows).setChecked(true);
+ } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[8])) { // Chrome on Windows.
+ menu.findItem(R.id.user_agent_chrome_on_windows).setChecked(true);
+ } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[9])) { // Edge on Windows.
+ menu.findItem(R.id.user_agent_edge_on_windows).setChecked(true);
+ } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[10])) { // Internet Explorer on Windows.
+ menu.findItem(R.id.user_agent_internet_explorer_on_windows).setChecked(true);
+ } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[11])) { // Safari on macOS.
+ menu.findItem(R.id.user_agent_safari_on_macos).setChecked(true);
+ } else { // Custom user agent.
+ menu.findItem(R.id.user_agent_custom).setChecked(true);
+ }
+
// Initialize font size variables.
int fontSize = mainWebView.getSettings().getTextZoom();
String fontSizeTitle;
WebStorage webStorage = WebStorage.getInstance();
webStorage.deleteAllData();
- // Manually delete the DOM storage files and directories.
- try {
- // A `String[]` must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
- privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
-
- // Multiple commands must be used because `Runtime.exec()` does not like `*`.
- privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
- privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
- privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
- privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
- } catch (IOException e) {
- // Do nothing if an error is thrown.
- }
+ // Initialize a handler to manually delete the DOM storage files and directories.
+ Handler deleteDomStorageHandler = new Handler();
+
+ // Setup a runnable to manually delete the DOM storage files and directories.
+ Runnable deleteDomStorageRunnable = () -> {
+ try {
+ // A `String[]` must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
+ privacyBrowserRuntime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
+
+ // Multiple commands must be used because `Runtime.exec()` does not like `*`.
+ privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
+ privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
+ privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
+ privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
+ } catch (IOException e) {
+ // Do nothing if an error is thrown.
+ }
+ };
+
+ // Manually delete the DOM storage files after 200 milliseconds.
+ deleteDomStorageHandler.postDelayed(deleteDomStorageRunnable, 200);
}
}
})
.show();
return true;
+ case R.id.easylist:
+ // Toggle the EasyList status.
+ easyListEnabled = !easyListEnabled;
+
+ // Update the menu checkbox.
+ menuItem.setChecked(easyListEnabled);
+
+ // Reload the main WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.easyprivacy:
+ // Toggle the EasyPrivacy status.
+ easyPrivacyEnabled = !easyPrivacyEnabled;
+
+ // Update the menu checkbox.
+ menuItem.setChecked(easyPrivacyEnabled);
+
+ // Reload the main WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.fanboys_annoyance_list:
+ // Toggle Fanboy's Annoyance List status.
+ fanboysAnnoyanceListEnabled = !fanboysAnnoyanceListEnabled;
+
+ // Update the menu checkbox.
+ menuItem.setChecked(fanboysAnnoyanceListEnabled);
+
+ // Update the staus of Fanboy's Social Blocking List.
+ MenuItem fanboysSocialBlockingListMenuItem = mainMenu.findItem(R.id.fanboys_social_blocking_list);
+ fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListEnabled);
+
+ // Reload the main WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.fanboys_social_blocking_list:
+ // Toggle Fanboy's Social Blocking List status.
+ fanboysSocialBlockingListEnabled = !fanboysSocialBlockingListEnabled;
+
+ // Update the menu checkbox.
+ menuItem.setChecked(fanboysSocialBlockingListEnabled);
+
+ // Reload the main WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.ultraprivacy:
+ // Toggle the UltraPrivacy status.
+ ultraPrivacyEnabled = !ultraPrivacyEnabled;
+
+ // Update the menu checkbox.
+ menuItem.setChecked(ultraPrivacyEnabled);
+
+ // Reload the main WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.block_all_third_party_requests:
+ //Toggle the third-party requests blocker status.
+ blockAllThirdPartyRequests = !blockAllThirdPartyRequests;
+
+ // Update the menu checkbox.
+ menuItem.setChecked(blockAllThirdPartyRequests);
+
+ // Reload the main WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.user_agent_privacy_browser:
+ // Update the user agent.
+ mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[0]);
+
+ // Reload the WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.user_agent_webview_default:
+ // Update the user agent.
+ mainWebView.getSettings().setUserAgentString("");
+
+ // Reload the WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.user_agent_firefox_on_android:
+ // Update the user agent.
+ mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[2]);
+
+ // Reload the WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.user_agent_chrome_on_android:
+ // Update the user agent.
+ mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[3]);
+
+ // Reload the WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.user_agent_safari_on_ios:
+ // Update the user agent.
+ mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[4]);
+
+ // Reload the WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.user_agent_firefox_on_linux:
+ // Update the user agent.
+ mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[5]);
+
+ // Reload the WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.user_agent_chromium_on_linux:
+ // Update the user agent.
+ mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[6]);
+
+ // Reload the WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.user_agent_firefox_on_windows:
+ // Update the user agent.
+ mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[7]);
+
+ // Reload the WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.user_agent_chrome_on_windows:
+ // Update the user agent.
+ mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[8]);
+
+ // Reload the WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.user_agent_edge_on_windows:
+ // Update the user agent.
+ mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[9]);
+
+ // Reload the WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.user_agent_internet_explorer_on_windows:
+ // Update the user agent.
+ mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[10]);
+
+ // Reload the WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.user_agent_safari_on_macos:
+ // Update the user agent.
+ mainWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[11]);
+
+ // Reload the WebView.
+ mainWebView.reload();
+ return true;
+
+ case R.id.user_agent_custom:
+ // Update the user agent.
+ mainWebView.getSettings().setUserAgentString(defaultCustomUserAgentString);
+
+ // Reload the WebView.
+ mainWebView.reload();
+ return true;
+
case R.id.font_size_twenty_five_percent:
mainWebView.getSettings().setTextZoom(25);
return true;
onTheFlyDisplayImagesSet = true;
return true;
+ case R.id.night_mode:
+ // Toggle night mode.
+ nightMode = !nightMode;
+
+ // Enable or disable JavaScript according to night mode, the global preference, and any domain settings.
+ if (nightMode) { // Night mode is enabled. Enable JavaScript.
+ // Update the global variable.
+ javaScriptEnabled = true;
+ } else if (domainSettingsApplied) { // Night mode is disabled and domain settings are applied. Set JavaScript according to the domain settings.
+ // Get the JavaScript preference that was stored the last time domain settings were loaded.
+ javaScriptEnabled = domainSettingsJavaScriptEnabled;
+ } else { // Night mode is disabled and domain settings are not applied. Set JavaScript according to the global preference.
+ // Get a handle for the shared preference.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Get the JavaScript preference.
+ javaScriptEnabled = sharedPreferences.getBoolean("javascript_enabled", false);
+ }
+
+ // Apply the JavaScript setting to the WebView.
+ mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
+
+ // Update the privacy icons.
+ updatePrivacyIcons(false);
+
+ // Reload the website.
+ mainWebView.reload();
+ return true;
+
+ case R.id.print:
+ // Get a `PrintManager` instance.
+ PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
+
+ // Convert `mainWebView` to `printDocumentAdapter`.
+ PrintDocumentAdapter printDocumentAdapter = mainWebView.createPrintDocumentAdapter();
+
+ // Remove the lint error below that `printManager` might be `null`.
+ assert printManager != null;
+
+ // Print the document. The print attributes are `null`.
+ printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
+ return true;
+
case R.id.view_source:
// Launch the View Source activity.
Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
startActivity(viewSourceIntent);
return true;
+ case R.id.proxy_through_orbot:
+ // Toggle the proxy through Orbot variable.
+ proxyThroughOrbot = !proxyThroughOrbot;
+
+ // Apply the proxy through Orbot settings.
+ applyProxyThroughOrbot(true);
+ return true;
+
case R.id.share:
// Setup the share string.
String shareString = webViewTitle + " – " + urlTextBox.getText().toString();
}, 200);
return true;
- case R.id.print:
- // Get a `PrintManager` instance.
- PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
-
- // Convert `mainWebView` to `printDocumentAdapter`.
- PrintDocumentAdapter printDocumentAdapter = mainWebView.createPrintDocumentAdapter();
-
- // Remove the lint error below that `printManager` might be `null`.
- assert printManager != null;
-
- // Print the document. The print attributes are `null`.
- printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
- return true;
-
case R.id.add_to_homescreen:
// Show the `CreateHomeScreenShortcutDialog` `AlertDialog` and name this instance `R.string.create_shortcut`.
AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog();
return true;
case R.id.refresh:
- mainWebView.reload();
+ if (menuItem.getTitle().equals(getString(R.string.refresh))) { // The refresh button was pushed.
+ // Reload the WebView.
+ mainWebView.reload();
+ } else { // The stop button was pushed.
+ // Stop the loading of the WebView.
+ mainWebView.stopLoading();
+ }
return true;
case R.id.ad_consent:
case R.id.back:
if (mainWebView.canGoBack()) {
+ // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+ formattedUrlString = "";
+
// Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
navigatingHistory = true;
case R.id.forward:
if (mainWebView.canGoForward()) {
+ // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+ formattedUrlString = "";
+
// Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
navigatingHistory = true;
urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history));
break;
+ case R.id.requests:
+ // Launch the requests activity.
+ Intent requestsIntent = new Intent(this, RequestsActivity.class);
+ startActivity(requestsIntent);
+ break;
+
case R.id.downloads:
// Launch the system Download Manager.
Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
reapplyDomainSettingsOnRestart = true;
currentDomainName = "";
- // Launch `DomainsActivity`.
+ // Launch the domains activity.
Intent domainsIntent = new Intent(this, DomainsActivity.class);
startActivity(domainsIntent);
break;
reapplyDomainSettingsOnRestart = true;
currentDomainName = "";
- // Launch `SettingsActivity`.
+ // Launch the settings activity.
Intent settingsIntent = new Intent(this, SettingsActivity.class);
startActivity(settingsIntent);
break;
+ case R.id.import_export:
+ // Launch the import/export activity.
+ Intent importExportIntent = new Intent (this, ImportExportActivity.class);
+ startActivity(importExportIntent);
+ break;
+
case R.id.guide:
// Launch `GuideActivity`.
Intent guideIntent = new Intent(this, GuideActivity.class);
break;
case R.id.clearAndExit:
+ // Close the bookmarks cursor and database.
+ bookmarksCursor.close();
+ bookmarksDatabaseHelper.close();
+
// Get a handle for `sharedPreferences`. `this` references the current context.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- // Reload the ad for the free flavor if we are not in full screen mode.
+ // Reload the ad for the free flavor if we not in full screen mode.
if (BuildConfig.FLAVOR.contentEquals("free") && !inFullScreenBrowsingMode) {
// Reload the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
- AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_id));
+ AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_unit_id));
}
// `invalidateOptionsMenu` should recalculate the number of action buttons from the menu to display on the app bar, but it doesn't because of the this bug:
// Show a dialog if the user has previously denied the permission.
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first.
- // Get a handle for the download location permission alert dialog and set the download type to DOWNLOAD_FILE.
+ // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_FILE.
DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_FILE);
// Show the download location permission alert dialog. The permission will be requested when the the dialog is closed.
// Show a dialog if the user has previously denied the permission.
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first.
- // Get a handle for the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE.
+ // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE.
DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_IMAGE);
// Show the download location permission alert dialog. The permission will be requested when the dialog is closed.
// Show a dialog if the user has previously denied the permission.
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first.
- // Get a handle for the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE.
+ // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE.
DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_IMAGE);
// Show the download location permission alert dialog. The permission will be requested when the dialog is closed.
}
@Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case DOWNLOAD_FILE_REQUEST_CODE:
// Show the download file alert dialog. When the dialog closes, the correct command will be used based on the permission status.
@Override
public void onSslMismatchBack() {
if (mainWebView.canGoBack()) { // There is a back page in the history.
+ // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+ formattedUrlString = "";
+
// Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
navigatingHistory = true;
@Override
public void onUrlHistoryEntrySelected(int moveBackOrForwardSteps) {
+ // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+ formattedUrlString = "";
+
// Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
navigatingHistory = true;
}
} else if (mainWebView.canGoBack()) { // There is at least one item in the `WebView` history.
+ // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+ formattedUrlString = "";
+
// Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
navigatingHistory = true;
// Check to see if `unformattedUrlString` is a valid URL. Otherwise, convert it into a search.
if ((Patterns.WEB_URL.matcher(unformattedUrlString).matches()) || (unformattedUrlString.startsWith("http://")) || (unformattedUrlString.startsWith("https://"))) {
- // Add `http://` at the beginning if it is missing. Otherwise the app will segfault.
+ // Add `https://` at the beginning if it is missing. Otherwise the app will segfault.
if (!unformattedUrlString.startsWith("http")) {
- unformattedUrlString = "http://" + unformattedUrlString;
+ unformattedUrlString = "https://" + unformattedUrlString;
}
// Initialize `unformattedUrl`.
loadUrl(formattedUrlString);
}
+ private void loadUrl(String url) {// Apply any custom domain settings.
+ // Set the URL as the formatted URL string so that checking third-party requests works correctly.
+ formattedUrlString = url;
- private void loadUrl(String url) {
- // Apply any custom domain settings.
+ // Apply the domain settings.
applyDomainSettings(url, true, false);
+ // If loading a website, set `urlIsLoading` to prevent changes in the user agent on websites with redirects from reloading the current website.
+ urlIsLoading = !url.equals("");
+
// Load the URL.
mainWebView.loadUrl(url, customHeaders);
-
- // Set `urlIsLoading` to prevent changes in the user agent on websites with redirects from reloading the current website.
- urlIsLoading = true;
}
public void findPreviousOnPage(View view) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
// Store the values from the shared preferences in variables.
- String homepageString = sharedPreferences.getString("homepage", "https://start.duckduckgo.com");
- String torHomepageString = sharedPreferences.getString("tor_homepage", "https://3g2upl4pq6kufc4m.onion");
- String torSearchString = sharedPreferences.getString("tor_search", "https://3g2upl4pq6kufc4m.onion/html/?q=");
- String torSearchCustomURLString = sharedPreferences.getString("tor_search_custom_url", "");
- String searchString = sharedPreferences.getString("search", "https://duckduckgo.com/html/?q=");
- String searchCustomURLString = sharedPreferences.getString("search_custom_url", "");
incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
proxyThroughOrbot = sharedPreferences.getBoolean("proxy_through_orbot", false);
translucentNavigationBarOnFullscreen = sharedPreferences.getBoolean("translucent_navigation_bar", true);
displayWebpageImagesBoolean = sharedPreferences.getBoolean("display_webpage_images", true);
- // Set the homepage, search, and proxy options.
- if (proxyThroughOrbot) { // Set the Tor options.
- // Set `torHomepageString` as `homepage`.
- homepage = torHomepageString;
-
- // If formattedUrlString is null assign the homepage to it.
- if (formattedUrlString == null) {
- formattedUrlString = homepage;
- }
-
- // Set the search URL.
- if (torSearchString.equals("Custom URL")) { // Get the custom URL string.
- searchURL = torSearchCustomURLString;
- } else { // Use the string from the pre-built list.
- searchURL = torSearchString;
- }
-
- // Set the proxy. `this` refers to the current activity where an `AlertDialog` might be displayed.
- OrbotProxyHelper.setProxy(getApplicationContext(), this, "localhost", "8118");
-
- // Set the `appBar` background to indicate proxying through Orbot is enabled. `this` refers to the context.
- if (darkTheme) {
- appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.dark_blue_30));
- } else {
- appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.blue_50));
- }
-
- // Display a message to the user if we are waiting on Orbot.
- if (!orbotStatus.equals("ON")) {
- // Set `waitingForOrbot`.
- waitingForOrbot = true;
-
- // Load a waiting page. `null` specifies no encoding, which defaults to ASCII.
- mainWebView.loadData(waitingForOrbotHTMLString, "text/html", null);
- }
- } else { // Set the non-Tor options.
- // Set `homepageString` as `homepage`.
- homepage = homepageString;
-
- // If formattedUrlString is null assign the homepage to it.
- if (formattedUrlString == null) {
- formattedUrlString = homepage;
- }
-
- // Set the search URL.
- if (searchString.equals("Custom URL")) { // Get the custom URL string.
- searchURL = searchCustomURLString;
- } else { // Use the string from the pre-built list.
- searchURL = searchString;
- }
-
- // Reset the proxy to default. The host is `""` and the port is `"0"`.
- OrbotProxyHelper.setProxy(getApplicationContext(), this, "", "0");
-
- // Set the default `appBar` background. `this` refers to the context.
- if (darkTheme) {
- appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_900));
- } else {
- appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_100));
- }
-
- // Reset `waitingForOrbot.
- waitingForOrbot = false;
- }
+ // Apply the proxy through Orbot settings.
+ applyProxyThroughOrbot(false);
// Set Do Not Track status.
if (doNotTrackEnabled) {
}
// Apply the appropriate full screen mode the `SYSTEM_UI` flags.
- if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) {
+ if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode.
if (hideSystemBarsOnFullscreen) { // Hide everything.
// Remove the translucent navigation setting if it is currently flagged.
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
*/
rootCoordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
} else { // Hide everything except the status and navigation bars.
+ // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`.
+ rootCoordinatorLayout.setSystemUiVisibility(0);
+
// Add the translucent status flag if it is unset.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
}
- } else { // Switch to normal viewing mode.
- // Reset `inFullScreenBrowsingMode` to `false`.
+ } 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 `appBar` if `findOnPageLinearLayout` is not visible.
// Show the `BannerAd` in the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
// Initialize the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
- AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getFragmentManager(), getString(R.string.ad_id));
+ AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id));
}
+ // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`.
+ rootCoordinatorLayout.setSystemUiVisibility(0);
+
// Remove the translucent navigation bar flag if it is set.
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
// Add the translucent status flag if it is unset. This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`.
- rootCoordinatorLayout.setSystemUiVisibility(0);
-
// Constrain `rootCoordinatorLayout` inside the status and navigation bars.
rootCoordinatorLayout.setFitsSystemWindows(true);
}
// The deprecated `.getDrawable()` must be used until the minimum API >= 21.
@SuppressWarnings("deprecation")
private void applyDomainSettings(String url, boolean resetFavoriteIcon, boolean reloadWebsite) {
- // Reset `navigatingHistory`.
- navigatingHistory = false;
-
// Parse the URL into a URI.
Uri uri = Uri.parse(url);
favoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(favoriteIconBitmap, 64, 64, true));
}
- // Initialize the database handler. `this` specifies the context. The two `nulls` do not specify the database name or a `CursorFactory`.
- // The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
+ // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0);
// Get a full cursor from `domainsDatabaseHelper`.
// Store the general preference information.
String defaultFontSizeString = sharedPreferences.getString("default_font_size", "100");
String defaultUserAgentName = sharedPreferences.getString("user_agent", "Privacy Browser");
- String defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0");
+ defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0");
boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
nightMode = sharedPreferences.getBoolean("night_mode", false);
easyPrivacyEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1);
fanboysAnnoyanceListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1);
fanboysSocialBlockingListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1);
+ ultraPrivacyEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)) == 1);
+ blockAllThirdPartyRequests = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1);
String userAgentName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
int fontSize = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
int swipeToRefreshInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
break;
}
- // Set `javaScriptEnabled` to be `true` if `night_mode` is `true`.
+ // Store the domain JavaScript status. This is used by the options menu night mode toggle.
+ domainSettingsJavaScriptEnabled = javaScriptEnabled;
+
+ // Enable JavaScript if night mode is enabled.
if (nightMode) {
javaScriptEnabled = true;
}
} else {
urlAppBarRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
}
- } else { // The URL we are loading does not have custom domain settings. Load the defaults.
+ } else { // The new URL does not have custom domain settings. Load the defaults.
// Store the values from `sharedPreferences` in variables.
javaScriptEnabled = sharedPreferences.getBoolean("javascript_enabled", false);
firstPartyCookiesEnabled = sharedPreferences.getBoolean("first_party_cookies_enabled", false);
easyPrivacyEnabled = sharedPreferences.getBoolean("easyprivacy", true);
fanboysAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboy_annoyance_list", true);
fanboysSocialBlockingListEnabled = sharedPreferences.getBoolean("fanboy_social_blocking_list", true);
+ ultraPrivacyEnabled = sharedPreferences.getBoolean("ultraprivacy", true);
+ blockAllThirdPartyRequests = sharedPreferences.getBoolean("block_all_third_party_requests", false);
// Set `javaScriptEnabled` to be `true` if `night_mode` is `true`.
if (nightMode) {
urlAppBarRelativeLayout.setBackgroundDrawable(getResources().getDrawable(R.color.transparent));
}
- // Close `domainsDatabaseHelper`.
+ // Close the domains database helper.
domainsDatabaseHelper.close();
// Remove the `onTheFlyDisplayImagesSet` flag and set the display webpage images mode. `true` indicates that custom domain settings are applied.
if (mainMenu != null) {
updatePrivacyIcons(true);
}
+ }
+
+ // Reload the website if returning from the Domains activity.
+ if (reloadWebsite) {
+ mainWebView.reload();
+ }
+ }
+
+ private void applyProxyThroughOrbot(boolean reloadWebsite) {
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Get the search preferences.
+ String homepageString = sharedPreferences.getString("homepage", "https://searx.me/");
+ String torHomepageString = sharedPreferences.getString("tor_homepage", "http://ulrn6sryqaifefld.onion/");
+ String torSearchString = sharedPreferences.getString("tor_search", "http://ulrn6sryqaifefld.onion/?q=");
+ String torSearchCustomUrlString = sharedPreferences.getString("tor_search_custom_url", "");
+ String searchString = sharedPreferences.getString("search", "https://searx.me/?q=");
+ String searchCustomUrlString = sharedPreferences.getString("search_custom_url", "");
+
+ // Set the homepage, search, and proxy options.
+ if (proxyThroughOrbot) { // Set the Tor options.
+ // Set `torHomepageString` as `homepage`.
+ homepage = torHomepageString;
+
+ // If formattedUrlString is null assign the homepage to it.
+ if (formattedUrlString == null) {
+ formattedUrlString = homepage;
+ }
+
+ // Set the search URL.
+ if (torSearchString.equals("Custom URL")) { // Get the custom URL string.
+ searchURL = torSearchCustomUrlString;
+ } else { // Use the string from the pre-built list.
+ searchURL = torSearchString;
+ }
+
+ // Set the proxy. `this` refers to the current activity where an `AlertDialog` might be displayed.
+ OrbotProxyHelper.setProxy(getApplicationContext(), this, "localhost", "8118");
+
+ // Set the `appBar` background to indicate proxying through Orbot is enabled. `this` refers to the context.
+ if (darkTheme) {
+ appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.dark_blue_30));
+ } else {
+ appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.blue_50));
+ }
+
+ // Check to see if Orbot is ready.
+ if (!orbotStatus.equals("ON")) { // Orbot is not ready.
+ // Set `waitingForOrbot`.
+ waitingForOrbot = true;
+
+ // Disable the wide view port so that the waiting for Orbot text is displayed correctly.
+ mainWebView.getSettings().setUseWideViewPort(false);
+
+ // Load a waiting page. `null` specifies no encoding, which defaults to ASCII.
+ mainWebView.loadData(waitingForOrbotHtmlString, "text/html", null);
+ } else if (reloadWebsite) { // Orbot is ready and the website should be reloaded.
+ // Reload the website.
+ mainWebView.reload();
+ }
+ } else { // Set the non-Tor options.
+ // Set `homepageString` as `homepage`.
+ homepage = homepageString;
+
+ // If formattedUrlString is null assign the homepage to it.
+ if (formattedUrlString == null) {
+ formattedUrlString = homepage;
+ }
+
+ // Set the search URL.
+ if (searchString.equals("Custom URL")) { // Get the custom URL string.
+ searchURL = searchCustomUrlString;
+ } else { // Use the string from the pre-built list.
+ searchURL = searchString;
+ }
+
+ // Reset the proxy to default. The host is `""` and the port is `"0"`.
+ OrbotProxyHelper.setProxy(getApplicationContext(), this, "", "0");
+
+ // Set the default `appBar` background. `this` refers to the context.
+ if (darkTheme) {
+ appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_900));
+ } else {
+ appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_100));
+ }
+
+ // Reset `waitingForOrbot.
+ waitingForOrbot = false;
- // Reload the website if returning from the Domains activity.
+ // Reload the website if requested.
if (reloadWebsite) {
mainWebView.reload();
}