X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FMainWebViewActivity.java;h=7c13950a9334030c6d4d86a608046e4508975e96;hp=26a6a403a68256af7eb0bdfac55acc56edb545ca;hb=de29e5f5b80d44fdc50bbb21379db860a7c7dfa2;hpb=5a385f0ba12a7af005761e30155f7fd18767d2f9 diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java index 26a6a403..7c13950a 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -23,6 +23,7 @@ package com.stoutner.privacybrowser.activities; import android.Manifest; import android.annotation.SuppressLint; +import android.app.Activity; import android.app.DialogFragment; import android.app.DownloadManager; import android.content.ActivityNotFoundException; @@ -173,7 +174,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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()` and `onResume()`. public static String orbotStatus; // `webViewTitle` is public static so it can be accessed from `CreateBookmarkDialog` and `CreateHomeScreenShortcutDialog`. It is also used in `onCreate()`. @@ -194,12 +195,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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()`. + // 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 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; @@ -209,7 +219,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook public final static int REQUEST_DEFAULT = 0; public final static int REQUEST_ALLOWED = 1; - public final static int REQUEST_BLOCKED = 2; + 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; @@ -235,6 +246,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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 static String currentBookmarksFolder; @@ -313,7 +328,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `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()`. @@ -325,11 +340,33 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `searchURL` is used in `loadURLFromTextBox()` and `applyAppSettings()`. private String searchURL; - // The block list variables are used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, 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; @@ -358,18 +395,27 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `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 `applyAppSettings()`. 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; @@ -388,8 +434,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `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; @@ -417,13 +463,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `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()`. @@ -432,7 +479,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `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()`. @@ -517,12 +564,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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(); } @@ -555,13 +605,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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; @@ -682,7 +732,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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. @@ -852,7 +902,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; }); - // The `DrawerListener` is used 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) { @@ -868,27 +918,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onDrawerStateChanged(int newState) { - if ((newState == DrawerLayout.STATE_SETTLING) || (newState == DrawerLayout.STATE_DRAGGING)) { // The drawer is opening or closing. - // Initialize a the blocked requests counter. - int blockedRequests = 0; - - // Count the number of blocked requests. - for (int i = 0; i < resourceRequests.size(); i++) { - if (Integer.valueOf(resourceRequests.get(i)[REQUEST_DISPOSITION]) == REQUEST_BLOCKED) { - blockedRequests++; - } - } - + 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(getResources().getString(R.string.requests) + " - " + blockedRequests); + 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(); } } @@ -913,10 +953,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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) { @@ -924,7 +964,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } }; - // Use `displayWebViewHandler` to delay the displaying of `mainWebView` for 500 milliseconds. + // Displaying of `mainWebView` after 500 milliseconds. displayWebViewHandler.postDelayed(displayWebViewRunnable, 500); }); } @@ -975,6 +1015,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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. @@ -996,6 +1039,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); @@ -1004,20 +1050,77 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); - // Set `rootCoordinatorLayout` to fit inside the status and navigation bars. This also clears the `SYSTEM_UI` flags. - rootCoordinatorLayout.setFitsSystemWindows(true); + // 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); + + // 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)); } } @@ -1055,7 +1158,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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. @@ -1064,7 +1167,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); @@ -1094,7 +1197,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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. @@ -1126,6 +1229,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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); @@ -1156,14 +1262,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Parse the block lists. final ArrayList> easyList = blockListHelper.parseBlockList(getAssets(), "blocklists/easylist.txt"); final ArrayList> easyPrivacy = blockListHelper.parseBlockList(getAssets(), "blocklists/easyprivacy.txt"); - final ArrayList> fanboyAnnoyance = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-annoyance.txt"); - final ArrayList> fanboySocial = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-social.txt"); + final ArrayList> fanboysAnnoyanceList = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-annoyance.txt"); + final ArrayList> fanboysSocialList = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-social.txt"); + final ArrayList> 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. @@ -1172,6 +1283,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith("http")) { // Load the URL in Privacy Browser. + // 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. `applyDomainSettings` doesn't do anything if the domain has not changed. applyDomainSettings(url, true, false); @@ -1239,12 +1353,112 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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 `whiteListResultStringArray`. + // 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; } @@ -1252,7 +1466,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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; } @@ -1260,21 +1488,49 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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. + // 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 defult request. + } else { // The request didn't match any blocklist entry. Log it as a default request. resourceRequests.add(new String[]{String.valueOf(REQUEST_DEFAULT), url}); } @@ -1299,6 +1555,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); @@ -1320,22 +1585,61 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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; @@ -1505,12 +1809,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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)) { @@ -1610,26 +1913,63 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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 @@ -1649,7 +1989,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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 @@ -1662,11 +2009,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); @@ -1676,6 +2026,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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; } @@ -1691,13 +2056,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook MenuItem clearCookiesMenuItem = menu.findItem(R.id.clear_cookies); MenuItem clearDOMStorageMenuItem = menu.findItem(R.id.clear_dom_storage); MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data); // Form data can be removed once the minimum API >= 26. - MenuItem easyListMenuItem = menu.findItem(R.id.easylist); - MenuItem easyPrivacyMenuItem = menu.findItem(R.id.easyprivacy); - MenuItem fanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list); - MenuItem fanboysSocialBlockingListMenuItem = menu.findItem(R.id.fanboys_social_blocking_list); 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); // Set the text for the domain menu item. if (domainSettingsApplied) { @@ -1715,8 +2077,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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); // Enable third-party cookies if first-party cookies are enabled. toggleThirdPartyCookiesMenuItem.setEnabled(firstPartyCookiesEnabled); @@ -1748,6 +2113,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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. @@ -1756,6 +2124,47 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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; @@ -2043,19 +2452,27 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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); } } }) @@ -2088,6 +2505,180 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook .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; @@ -2137,58 +2728,39 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook onTheFlyDisplayImagesSet = true; 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.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.night_mode: + // Toggle night mode. + nightMode = !nightMode; - 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; + // 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); + } - // Update the menu checkbox. - menuItem.setChecked(fanboysAnnoyanceListEnabled); + // Apply the JavaScript setting to the WebView. + mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled); - // Update the staus of Fanboy's Social Blocking List. - MenuItem fanboysSocialBlockingListMenuItem = mainMenu.findItem(R.id.fanboys_social_blocking_list); - fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListEnabled); + // Update the privacy icons. + updatePrivacyIcons(false); - // Reload the main WebView. + // Reload the website. mainWebView.reload(); return true; - case R.id.fanboys_social_blocking_list: - // Toggle Fanboy's Social Blocking List status. - fanboysSocialBlockingListEnabled = !fanboysSocialBlockingListEnabled; - - // Update teh menu checkbox. - menuItem.setChecked(fanboysSocialBlockingListEnabled); - - // Reload the main WebView. - mainWebView.reload(); + case R.id.view_source: + // Launch the View Source activity. + Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class); + startActivity(viewSourceIntent); return true; case R.id.share: @@ -2246,7 +2818,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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: @@ -2274,6 +2852,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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; @@ -2284,6 +2865,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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; @@ -2340,6 +2924,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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); @@ -2353,6 +2943,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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); @@ -2488,10 +3082,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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: @@ -2552,7 +3146,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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. @@ -2637,7 +3231,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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. @@ -2696,7 +3290,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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. @@ -2849,7 +3443,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @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. @@ -3117,6 +3711,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @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; @@ -3136,6 +3733,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @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; @@ -3168,6 +3768,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } 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; @@ -3195,9 +3798,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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`. @@ -3242,10 +3845,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } 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; + + // Apply the domain settings. applyDomainSettings(url, true, false); - // Set `urlIsLoading` to prevent changes in the user agent on websites with redirects from reloading the current website. - urlIsLoading = true; + // 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); @@ -3283,11 +3890,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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 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://duckduckgo.com/html/?q="); + String searchString = sharedPreferences.getString("search", "https://searx.me/?q="); String searchCustomURLString = sharedPreferences.getString("search_custom_url", ""); incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false); boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false); @@ -3324,11 +3931,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.blue_50)); } - // Display a message to the user if we are waiting on Orbot. + // Display a message to the user if waiting for Orbot. if (!orbotStatus.equals("ON")) { // 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); } @@ -3370,7 +3980,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // 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); @@ -3387,6 +3997,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook */ 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); @@ -3398,8 +4011,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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. @@ -3410,18 +4023,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); } @@ -3431,9 +4044,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); @@ -3471,8 +4081,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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`. @@ -3527,7 +4136,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); @@ -3548,6 +4157,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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)); @@ -3572,7 +4183,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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; } @@ -3703,6 +4317,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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) { @@ -3774,7 +4390,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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.