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=585a243e320b90e06367e29a8837234357205398;hb=de29e5f5b80d44fdc50bbb21379db860a7c7dfa2;hpb=3192bd34bf50f05fa3939565dd979d42020fb78d 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 585a243e..7c13950a 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -202,13 +202,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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; - int blockedRequests; - int easyListBlockedRequests; - int easyPrivacyBlockedRequests; - int fanboysAnnoyanceListBlockedRequests; - int fanboysSocialBlockingListBlockedRequests; - int ultraPrivacyBlockedRequests; - int thirdPartyBlockedRequests; + 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; @@ -328,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()`. @@ -340,6 +340,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `searchURL` is used in `loadURLFromTextBox()` and `applyAppSettings()`. private String searchURL; + // `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; @@ -347,6 +362,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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; @@ -383,7 +404,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `ignorePinnedSslCertificateForDomain` is used in `onCreate()`, `onSslMismatchProceed()`, and `applyDomainSettings()`. private boolean ignorePinnedSslCertificate; - // `orbotStatusBroadcastReciever` is used in `onCreate()` and `onDestroy()`. + // `orbotStatusBroadcastReceiver` is used in `onCreate()` and `onDestroy()`. private BroadcastReceiver orbotStatusBroadcastReceiver; // `waitingForOrbot` is used in `onCreate()`, `onResume()`, and `applyAppSettings()`. @@ -392,6 +413,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `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; @@ -410,20 +434,6 @@ 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; - - // `refreshMenuItem` is used in `onCreate()` and `onCreateOptionsMenu()`. - private MenuItem refreshMenuItem; - - // The blocklist menu items are used in `onCreate()` and `onCreateOptionsMenu()`. - MenuItem easyListMenuItem; - MenuItem easyPrivacyMenuItem; - MenuItem fanboysAnnoyanceListMenuItem; - MenuItem fanboysSocialBlockingListMenuItem; - MenuItem ultraPrivacyMenuItem; - MenuItem blockAllThirdParyRequestsMenuItem; - // `displayAdditionalAppBarIcons` is used in `onCreate()` and `onCreateOptionsMenu()`. private boolean displayAdditionalAppBarIcons; @@ -459,7 +469,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `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()`. @@ -468,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()`. @@ -553,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(); } @@ -597,7 +611,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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; @@ -718,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. @@ -939,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) { @@ -950,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); }); } @@ -1037,7 +1051,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onHideCustomView() { // Unset the full screen video flag. - displayingFullScreenVideo = true; + displayingFullScreenVideo = false; // Hide `fullScreenVideoFrameLayout`. fullScreenVideoFrameLayout.removeAllViews(); @@ -1087,7 +1101,7 @@ 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`. @@ -1106,7 +1120,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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)); } } @@ -1144,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. @@ -1153,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); @@ -1215,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); @@ -1386,7 +1403,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); - blockAllThirdParyRequestsMenuItem.setTitle(thirdPartyBlockedRequests + " - " + getString(R.string.block_all_third_party_requests)); + blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests); + blockAllThirdPartyRequestsMenuItem.setTitle(thirdPartyBlockedRequests + " - " + getString(R.string.block_all_third_party_requests)); }); // Add the request to the log. @@ -1406,6 +1424,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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)); }); @@ -1433,6 +1452,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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)); }); @@ -1454,6 +1474,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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)); }); @@ -1475,6 +1496,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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)); }); @@ -1493,6 +1515,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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)); }); @@ -1507,7 +1530,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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}); } @@ -1592,6 +1615,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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(); @@ -1891,6 +1919,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); } @@ -1933,6 +1964,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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(); } @@ -1955,12 +1990,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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. 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); - blockAllThirdParyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests); + 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 @@ -1990,14 +2026,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); } - // Update the display names for the blocklists with the number of blocked requests. - 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)); - blockAllThirdParyRequestsMenuItem.setTitle(thirdPartyBlockedRequests + " - " + getString(R.string.block_all_third_party_requests)); - // Replace Refresh with Stop if a URL is already loading. if (urlIsLoading) { // Set the title. @@ -2031,6 +2059,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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) { @@ -2049,9 +2078,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook fanboysAnnoyanceListMenuItem.setChecked(fanboysAnnoyanceListEnabled); fanboysSocialBlockingListMenuItem.setChecked(fanboysSocialBlockingListEnabled); ultraPrivacyMenuItem.setChecked(ultraPrivacyEnabled); - blockAllThirdParyRequestsMenuItem.setChecked(blockAllThirdPartyRequests); + 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); @@ -2094,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; @@ -2381,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); } } }) @@ -2426,61 +2505,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook .show(); return true; - case R.id.font_size_twenty_five_percent: - mainWebView.getSettings().setTextZoom(25); - return true; - - case R.id.font_size_fifty_percent: - mainWebView.getSettings().setTextZoom(50); - return true; - - case R.id.font_size_seventy_five_percent: - mainWebView.getSettings().setTextZoom(75); - return true; - - case R.id.font_size_one_hundred_percent: - mainWebView.getSettings().setTextZoom(100); - return true; - - case R.id.font_size_one_hundred_twenty_five_percent: - mainWebView.getSettings().setTextZoom(125); - return true; - - case R.id.font_size_one_hundred_fifty_percent: - mainWebView.getSettings().setTextZoom(150); - return true; - - case R.id.font_size_one_hundred_seventy_five_percent: - mainWebView.getSettings().setTextZoom(175); - return true; - - case R.id.font_size_two_hundred_percent: - mainWebView.getSettings().setTextZoom(200); - return true; - - case R.id.swipe_to_refresh: - // Toggle swipe to refresh. - swipeRefreshLayout.setEnabled(!swipeRefreshLayout.isEnabled()); - return true; - - case R.id.display_images: - if (mainWebView.getSettings().getLoadsImagesAutomatically()) { // Images are currently loaded automatically. - mainWebView.getSettings().setLoadsImagesAutomatically(false); - mainWebView.reload(); - } else { // Images are not currently loaded automatically. - mainWebView.getSettings().setLoadsImagesAutomatically(true); - } - - // Set `onTheFlyDisplayImagesSet`. - 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; @@ -2551,6 +2575,194 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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; + + case R.id.font_size_fifty_percent: + mainWebView.getSettings().setTextZoom(50); + return true; + + case R.id.font_size_seventy_five_percent: + mainWebView.getSettings().setTextZoom(75); + return true; + + case R.id.font_size_one_hundred_percent: + mainWebView.getSettings().setTextZoom(100); + return true; + + case R.id.font_size_one_hundred_twenty_five_percent: + mainWebView.getSettings().setTextZoom(125); + return true; + + case R.id.font_size_one_hundred_fifty_percent: + mainWebView.getSettings().setTextZoom(150); + return true; + + case R.id.font_size_one_hundred_seventy_five_percent: + mainWebView.getSettings().setTextZoom(175); + return true; + + case R.id.font_size_two_hundred_percent: + mainWebView.getSettings().setTextZoom(200); + return true; + + case R.id.swipe_to_refresh: + // Toggle swipe to refresh. + swipeRefreshLayout.setEnabled(!swipeRefreshLayout.isEnabled()); + return true; + + case R.id.display_images: + if (mainWebView.getSettings().getLoadsImagesAutomatically()) { // Images are currently loaded automatically. + mainWebView.getSettings().setLoadsImagesAutomatically(false); + mainWebView.reload(); + } else { // Images are not currently loaded automatically. + mainWebView.getSettings().setLoadsImagesAutomatically(true); + } + + // Set `onTheFlyDisplayImagesSet`. + 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.view_source: + // Launch the View Source activity. + Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class); + startActivity(viewSourceIntent); + return true; + case R.id.share: // Setup the share string. String shareString = webViewTitle + " – " + urlTextBox.getText().toString(); @@ -2712,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); @@ -2725,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); @@ -2863,7 +3085,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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: @@ -2924,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. @@ -3009,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. @@ -3068,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. @@ -3221,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. @@ -3576,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`. @@ -3714,6 +3936,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); } @@ -3798,7 +4023,7 @@ 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`. @@ -3856,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`. @@ -3912,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); @@ -3959,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; } @@ -4163,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.