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=e393e31082470dfdedcb4a9fd01e3492bed332dc;hp=9f51f749dcbb26993e1eb87fcb1b4cd3b383cc24;hb=39380e8e8bdb3b9e29569a263277c9c3112b44ac;hpb=86e63c8ed007311ab392d4beb7dd7ba64b9c3c70 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 9f51f749..e393e310 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -1059,960 +1059,755 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - // Remove Android Studio's warning about the dangers of enabling JavaScript. We know. Oh, how we know. - @SuppressLint("SetJavaScriptEnabled") public boolean onOptionsItemSelected(MenuItem menuItem) { - // Get the selected menu item ID. - int menuItemId = menuItem.getItemId(); - // Get a handle for the shared preferences. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); // Get a handle for the cookie manager. CookieManager cookieManager = CookieManager.getInstance(); - // Run the commands that correlate to the selected menu item. - switch (menuItemId) { - case R.id.toggle_javascript: - // Toggle the JavaScript status. - currentWebView.getSettings().setJavaScriptEnabled(!currentWebView.getSettings().getJavaScriptEnabled()); - - // Update the privacy icon. `true` runs `invalidateOptionsMenu` as the last step. - updatePrivacyIcons(true); - - // Display a `Snackbar`. - if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScrip is enabled. - Snackbar.make(webViewPager, R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show(); - } else if (cookieManager.acceptCookie()) { // JavaScript is disabled, but first-party cookies are enabled. - Snackbar.make(webViewPager, R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show(); - } else { // Privacy mode. - Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show(); - } - - // Reload the current WebView. - currentWebView.reload(); - - // Consume the event. - return true; - - case R.id.refresh: - if (menuItem.getTitle().equals(getString(R.string.refresh))) { // The refresh button was pushed. - // Reload the current WebView. - currentWebView.reload(); - } else { // The stop button was pushed. - // Stop the loading of the WebView. - currentWebView.stopLoading(); - } - - // Consume the event. - return true; - - case R.id.bookmarks: - // Open the bookmarks drawer. - drawerLayout.openDrawer(GravityCompat.END); - - // Consume the event. - return true; - - case R.id.toggle_first_party_cookies: - // Switch the first-party cookie status. - cookieManager.setAcceptCookie(!cookieManager.acceptCookie()); + // Get the selected menu item ID. + int menuItemId = menuItem.getItemId(); - // Store the first-party cookie status. - currentWebView.setAcceptFirstPartyCookies(cookieManager.acceptCookie()); + // Run the commands that correlate to the selected menu item. + if (menuItemId == R.id.toggle_javascript) { // JavaScript. + // Toggle the JavaScript status. + currentWebView.getSettings().setJavaScriptEnabled(!currentWebView.getSettings().getJavaScriptEnabled()); - // Update the menu checkbox. - menuItem.setChecked(cookieManager.acceptCookie()); + // Update the privacy icon. + updatePrivacyIcons(true); - // Update the privacy icon. `true` runs `invalidateOptionsMenu` as the last step. - updatePrivacyIcons(true); + // Display a `Snackbar`. + if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScrip is enabled. + Snackbar.make(webViewPager, R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show(); + } else if (cookieManager.acceptCookie()) { // JavaScript is disabled, but first-party cookies are enabled. + Snackbar.make(webViewPager, R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show(); + } else { // Privacy mode. + Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show(); + } - // Display a snackbar. - if (cookieManager.acceptCookie()) { // First-party cookies are enabled. - Snackbar.make(webViewPager, R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show(); - } else if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is still enabled. - Snackbar.make(webViewPager, R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show(); - } else { // Privacy mode. - Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show(); - } + // Reload the current WebView. + currentWebView.reload(); + // Consume the event. + return true; + } else if (menuItemId == R.id.refresh) { // Refresh. + // Run the command that correlates to the current status of the menu item. + if (menuItem.getTitle().equals(getString(R.string.refresh))) { // The refresh button was pushed. // Reload the current WebView. currentWebView.reload(); + } else { // The stop button was pushed. + // Stop the loading of the WebView. + currentWebView.stopLoading(); + } - // Consume the event. - return true; + // Consume the event. + return true; + } else if (menuItemId == R.id.bookmarks) { // Bookmarks. + // Open the bookmarks drawer. + drawerLayout.openDrawer(GravityCompat.END); - case R.id.toggle_third_party_cookies: - if (Build.VERSION.SDK_INT >= 21) { - // Switch the status of thirdPartyCookiesEnabled. - cookieManager.setAcceptThirdPartyCookies(currentWebView, !cookieManager.acceptThirdPartyCookies(currentWebView)); + // Consume the event. + return true; + } else if (menuItemId == R.id.toggle_first_party_cookies) { // First-party cookies. + // Switch the first-party cookie status. + cookieManager.setAcceptCookie(!cookieManager.acceptCookie()); - // Update the menu checkbox. - menuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView)); + // Store the first-party cookie status. + currentWebView.setAcceptFirstPartyCookies(cookieManager.acceptCookie()); - // Display a snackbar. - if (cookieManager.acceptThirdPartyCookies(currentWebView)) { - Snackbar.make(webViewPager, R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show(); - } else { - Snackbar.make(webViewPager, R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show(); - } + // Update the menu checkbox. + menuItem.setChecked(cookieManager.acceptCookie()); - // Reload the current WebView. - currentWebView.reload(); - } // Else do nothing because SDK < 21. + // Update the privacy icon. + updatePrivacyIcons(true); - // Consume the event. - return true; + // Display a snackbar. + if (cookieManager.acceptCookie()) { // First-party cookies are enabled. + Snackbar.make(webViewPager, R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show(); + } else if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is still enabled. + Snackbar.make(webViewPager, R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show(); + } else { // Privacy mode. + Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show(); + } - case R.id.toggle_dom_storage: - // Toggle the status of domStorageEnabled. - currentWebView.getSettings().setDomStorageEnabled(!currentWebView.getSettings().getDomStorageEnabled()); + // Reload the current WebView. + currentWebView.reload(); - // Update the menu checkbox. - menuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled()); + // Consume the event. + return true; + } else if (menuItemId == R.id.toggle_third_party_cookies) { // Third-party cookies. + // Only act if the API >= 21. Otherwise, there are no third-party cookie controls. + if (Build.VERSION.SDK_INT >= 21) { + // Toggle the status of thirdPartyCookiesEnabled. + cookieManager.setAcceptThirdPartyCookies(currentWebView, !cookieManager.acceptThirdPartyCookies(currentWebView)); - // Update the privacy icon. `true` refreshes the app bar icons. - updatePrivacyIcons(true); + // Update the menu checkbox. + menuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView)); // Display a snackbar. - if (currentWebView.getSettings().getDomStorageEnabled()) { - Snackbar.make(webViewPager, R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show(); + if (cookieManager.acceptThirdPartyCookies(currentWebView)) { + Snackbar.make(webViewPager, R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show(); } else { - Snackbar.make(webViewPager, R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(webViewPager, R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show(); } // Reload the current WebView. currentWebView.reload(); + } - // Consume the event. - return true; + // Consume the event. + return true; + } else if (menuItemId == R.id.toggle_dom_storage) { // DOM storage. + // Toggle the status of domStorageEnabled. + currentWebView.getSettings().setDomStorageEnabled(!currentWebView.getSettings().getDomStorageEnabled()); - // Form data can be removed once the minimum API >= 26. - case R.id.toggle_save_form_data: - // Switch the status of saveFormDataEnabled. - currentWebView.getSettings().setSaveFormData(!currentWebView.getSettings().getSaveFormData()); + // Update the menu checkbox. + menuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled()); - // Update the menu checkbox. - menuItem.setChecked(currentWebView.getSettings().getSaveFormData()); - - // Display a snackbar. - if (currentWebView.getSettings().getSaveFormData()) { - Snackbar.make(webViewPager, R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show(); - } else { - Snackbar.make(webViewPager, R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show(); - } + // Update the privacy icon. + updatePrivacyIcons(true); - // Update the privacy icon. `true` runs `invalidateOptionsMenu` as the last step. - updatePrivacyIcons(true); + // Display a snackbar. + if (currentWebView.getSettings().getDomStorageEnabled()) { + Snackbar.make(webViewPager, R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show(); + } else { + Snackbar.make(webViewPager, R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show(); + } - // Reload the current WebView. - currentWebView.reload(); + // Reload the current WebView. + currentWebView.reload(); - // Consume the event. - return true; + // Consume the event. + return true; + } else if (menuItemId == R.id.toggle_save_form_data) { // Form data. This can be removed once the minimum API >= 26. + // Switch the status of saveFormDataEnabled. + currentWebView.getSettings().setSaveFormData(!currentWebView.getSettings().getSaveFormData()); - case R.id.clear_cookies: - Snackbar.make(webViewPager, R.string.cookies_deleted, Snackbar.LENGTH_LONG) - .setAction(R.string.undo, v -> { - // Do nothing because everything will be handled by `onDismissed()` below. - }) - .addCallback(new Snackbar.Callback() { - @SuppressLint("SwitchIntDef") // Ignore the lint warning about not handling the other possible events as they are covered by `default:`. - @Override - public void onDismissed(Snackbar snackbar, int event) { - if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed. - // Delete the cookies, which command varies by SDK. - if (Build.VERSION.SDK_INT < 21) { - cookieManager.removeAllCookie(); - } else { - cookieManager.removeAllCookies(null); - } - } - } - }) - .show(); + // Update the menu checkbox. + menuItem.setChecked(currentWebView.getSettings().getSaveFormData()); - // Consume the event. - return true; + // Display a snackbar. + if (currentWebView.getSettings().getSaveFormData()) { + Snackbar.make(webViewPager, R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show(); + } else { + Snackbar.make(webViewPager, R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show(); + } - case R.id.clear_dom_storage: - Snackbar.make(webViewPager, R.string.dom_storage_deleted, Snackbar.LENGTH_LONG) - .setAction(R.string.undo, v -> { - // Do nothing because everything will be handled by `onDismissed()` below. - }) - .addCallback(new Snackbar.Callback() { - @SuppressLint("SwitchIntDef") // Ignore the lint warning about not handling the other possible events as they are covered by `default:`. - @Override - public void onDismissed(Snackbar snackbar, int event) { - if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed. - // Delete the DOM Storage. - WebStorage webStorage = WebStorage.getInstance(); - webStorage.deleteAllData(); - - // 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 { - // Get a handle for the runtime. - Runtime runtime = Runtime.getRuntime(); - - // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`, - // which links to `/data/data/com.stoutner.privacybrowser.standard`. - String privateDataDirectoryString = getApplicationInfo().dataDir; - - // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly. - Process deleteLocalStorageProcess = runtime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"}); - - // Multiple commands must be used because `Runtime.exec()` does not like `*`. - Process deleteIndexProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB"); - Process deleteQuotaManagerProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager"); - Process deleteQuotaManagerJournalProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal"); - Process deleteDatabasesProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases"); - - // Wait for the processes to finish. - deleteLocalStorageProcess.waitFor(); - deleteIndexProcess.waitFor(); - deleteQuotaManagerProcess.waitFor(); - deleteQuotaManagerJournalProcess.waitFor(); - deleteDatabasesProcess.waitFor(); - } catch (Exception exception) { - // Do nothing if an error is thrown. - } - }; - - // Manually delete the DOM storage files after 200 milliseconds. - deleteDomStorageHandler.postDelayed(deleteDomStorageRunnable, 200); - } - } - }) - .show(); + // Update the privacy icon. + updatePrivacyIcons(true); - // Consume the event. - return true; + // Reload the current WebView. + currentWebView.reload(); - // Form data can be remove once the minimum API >= 26. - case R.id.clear_form_data: - Snackbar.make(webViewPager, R.string.form_data_deleted, Snackbar.LENGTH_LONG) - .setAction(R.string.undo, v -> { - // Do nothing because everything will be handled by `onDismissed()` below. - }) - .addCallback(new Snackbar.Callback() { - @SuppressLint("SwitchIntDef") // Ignore the lint warning about not handling the other possible events as they are covered by `default:`. - @Override - public void onDismissed(Snackbar snackbar, int event) { - if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed. - // Delete the form data. - WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(getApplicationContext()); - mainWebViewDatabase.clearFormData(); + // Consume the event. + return true; + } else if (menuItemId == R.id.clear_cookies) { // Clear cookies. + // Create a snackbar. + Snackbar.make(webViewPager, R.string.cookies_deleted, Snackbar.LENGTH_LONG) + .setAction(R.string.undo, v -> { + // Do nothing because everything will be handled by `onDismissed()` below. + }) + .addCallback(new Snackbar.Callback() { + @Override + public void onDismissed(Snackbar snackbar, int event) { + if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed. + // Delete the cookies, which command varies by SDK. + if (Build.VERSION.SDK_INT < 21) { + cookieManager.removeAllCookie(); + } else { + cookieManager.removeAllCookies(null); } } - }) - .show(); - - // Consume the event. - return true; - - case R.id.easylist: - // Toggle the EasyList status. - currentWebView.enableBlocklist(NestedScrollWebView.EASYLIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST)); - - // Update the menu checkbox. - menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST)); - - // Reload the current WebView. - currentWebView.reload(); - - // Consume the event. - return true; - - case R.id.easyprivacy: - // Toggle the EasyPrivacy status. - currentWebView.enableBlocklist(NestedScrollWebView.EASYPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY)); - - // Update the menu checkbox. - menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY)); - - // Reload the current WebView. - currentWebView.reload(); - - // Consume the event. - return true; - - case R.id.fanboys_annoyance_list: - // Toggle Fanboy's Annoyance List status. - currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); - - // Update the menu checkbox. - menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); - - // Update the staus of Fanboy's Social Blocking List. - MenuItem fanboysSocialBlockingListMenuItem = optionsMenu.findItem(R.id.fanboys_social_blocking_list); - fanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); - - // Reload the current WebView. - currentWebView.reload(); - - // Consume the event. - return true; - - case R.id.fanboys_social_blocking_list: - // Toggle Fanboy's Social Blocking List status. - currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST)); - - // Update the menu checkbox. - menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST)); - - // Reload the current WebView. - currentWebView.reload(); - - // Consume the event. - return true; - - case R.id.ultralist: - // Toggle the UltraList status. - currentWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST)); - - // Update the menu checkbox. - menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST)); - - // Reload the current WebView. - currentWebView.reload(); - - // Consume the event. - return true; - - case R.id.ultraprivacy: - // Toggle the UltraPrivacy status. - currentWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY)); - - // Update the menu checkbox. - menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY)); - - // Reload the current WebView. - currentWebView.reload(); - - // Consume the event. - return true; - - case R.id.block_all_third_party_requests: - //Toggle the third-party requests blocker status. - currentWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, !currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)); - - // Update the menu checkbox. - menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)); - - // Reload the current WebView. - currentWebView.reload(); - - // Consume the event. - return true; - - case R.id.proxy_none: - // Update the proxy mode. - proxyMode = ProxyHelper.NONE; - - // Apply the proxy mode. - applyProxy(true); - - // Consume the event. - return true; - - case R.id.proxy_tor: - // Update the proxy mode. - proxyMode = ProxyHelper.TOR; - - // Apply the proxy mode. - applyProxy(true); - - // Consume the event. - return true; - - case R.id.proxy_i2p: - // Update the proxy mode. - proxyMode = ProxyHelper.I2P; - - // Apply the proxy mode. - applyProxy(true); - - // Consume the event. - return true; - - case R.id.proxy_custom: - // Update the proxy mode. - proxyMode = ProxyHelper.CUSTOM; - - // Apply the proxy mode. - applyProxy(true); - - // Consume the event. - return true; - - case R.id.user_agent_privacy_browser: - // Update the user agent. - currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[0]); - - // Reload the current WebView. - currentWebView.reload(); - - // Consume the event. - return true; - - case R.id.user_agent_webview_default: - // Update the user agent. - currentWebView.getSettings().setUserAgentString(""); - - // Reload the current WebView. - currentWebView.reload(); - - // Consume the event. - return true; - - case R.id.user_agent_firefox_on_android: - // Update the user agent. - currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[2]); - - // Reload the current WebView. - currentWebView.reload(); - - // Consume the event. - return true; - - case R.id.user_agent_chrome_on_android: - // Update the user agent. - currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[3]); - - // Reload the current WebView. - currentWebView.reload(); - - // Consume the event. - return true; - - case R.id.user_agent_safari_on_ios: - // Update the user agent. - currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[4]); - - // Reload the current WebView. - currentWebView.reload(); - - // Consume the event. - return true; - - case R.id.user_agent_firefox_on_linux: - // Update the user agent. - currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[5]); + } + }) + .show(); - // Reload the current WebView. - currentWebView.reload(); + // Consume the event. + return true; + } else if (menuItemId == R.id.clear_dom_storage) { // Clear DOM storage. + // Create a snackbar. + Snackbar.make(webViewPager, R.string.dom_storage_deleted, Snackbar.LENGTH_LONG) + .setAction(R.string.undo, v -> { + // Do nothing because everything will be handled by `onDismissed()` below. + }) + .addCallback(new Snackbar.Callback() { + @Override + public void onDismissed(Snackbar snackbar, int event) { + if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed. + // Delete the DOM Storage. + WebStorage webStorage = WebStorage.getInstance(); + webStorage.deleteAllData(); + + // 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 { + // Get a handle for the runtime. + Runtime runtime = Runtime.getRuntime(); + + // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`, + // which links to `/data/data/com.stoutner.privacybrowser.standard`. + String privateDataDirectoryString = getApplicationInfo().dataDir; + + // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly. + Process deleteLocalStorageProcess = runtime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"}); + + // Multiple commands must be used because `Runtime.exec()` does not like `*`. + Process deleteIndexProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB"); + Process deleteQuotaManagerProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager"); + Process deleteQuotaManagerJournalProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal"); + Process deleteDatabasesProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases"); + + // Wait for the processes to finish. + deleteLocalStorageProcess.waitFor(); + deleteIndexProcess.waitFor(); + deleteQuotaManagerProcess.waitFor(); + deleteQuotaManagerJournalProcess.waitFor(); + deleteDatabasesProcess.waitFor(); + } catch (Exception exception) { + // Do nothing if an error is thrown. + } + }; - // Consume the event. - return true; + // Manually delete the DOM storage files after 200 milliseconds. + deleteDomStorageHandler.postDelayed(deleteDomStorageRunnable, 200); + } + } + }) + .show(); - case R.id.user_agent_chromium_on_linux: - // Update the user agent. - currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[6]); + // Consume the event. + return true; + } else if (menuItemId == R.id.clear_form_data) { // Clear form data. This can be remove once the minimum API >= 26. + // Create a snackbar. + Snackbar.make(webViewPager, R.string.form_data_deleted, Snackbar.LENGTH_LONG) + .setAction(R.string.undo, v -> { + // Do nothing because everything will be handled by `onDismissed()` below. + }) + .addCallback(new Snackbar.Callback() { + @Override + public void onDismissed(Snackbar snackbar, int event) { + if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed. + // Get a handle for the webView database. + WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(getApplicationContext()); + + // Delete the form data. + webViewDatabase.clearFormData(); + } + } + }) + .show(); - // Reload the current WebView. - currentWebView.reload(); + // Consume the event. + return true; + } else if (menuItemId == R.id.easylist) { // EasyList. + // Toggle the EasyList status. + currentWebView.enableBlocklist(NestedScrollWebView.EASYLIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST)); - // Consume the event. - return true; + // Update the menu checkbox. + menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST)); - case R.id.user_agent_firefox_on_windows: - // Update the user agent. - currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[7]); + // Reload the current WebView. + currentWebView.reload(); - // Reload the current WebView. - currentWebView.reload(); + // Consume the event. + return true; + } else if (menuItemId == R.id.easyprivacy) { // EasyPrivacy. + // Toggle the EasyPrivacy status. + currentWebView.enableBlocklist(NestedScrollWebView.EASYPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY)); - // Consume the event. - return true; + // Update the menu checkbox. + menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY)); - case R.id.user_agent_chrome_on_windows: - // Update the user agent. - currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[8]); + // Reload the current WebView. + currentWebView.reload(); - // Reload the current WebView. - currentWebView.reload(); + // Consume the event. + return true; + } else if (menuItemId == R.id.fanboys_annoyance_list) { // Fanboy's Annoyance List. + // Toggle Fanboy's Annoyance List status. + currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); - // Consume the event. - return true; + // Update the menu checkbox. + menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); - case R.id.user_agent_edge_on_windows: - // Update the user agent. - currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[9]); + // Get a handle for the Fanboy's Social Block List menu item. + MenuItem fanboysSocialBlockingListMenuItem = optionsMenu.findItem(R.id.fanboys_social_blocking_list); - // Reload the current WebView. - currentWebView.reload(); + // Update the staus of Fanboy's Social Blocking List. + fanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); - // Consume the event. - return true; + // Reload the current WebView. + currentWebView.reload(); - case R.id.user_agent_internet_explorer_on_windows: - // Update the user agent. - currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[10]); + // Consume the event. + return true; + } else if (menuItemId == R.id.fanboys_social_blocking_list) { // Fanboy's Social Blocking List. + // Toggle Fanboy's Social Blocking List status. + currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST)); - // Reload the current WebView. - currentWebView.reload(); + // Update the menu checkbox. + menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST)); - // Consume the event. - return true; + // Reload the current WebView. + currentWebView.reload(); - case R.id.user_agent_safari_on_macos: - // Update the user agent. - currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[11]); + // Consume the event. + return true; + } else if (menuItemId == R.id.ultralist) { // UltraList. + // Toggle the UltraList status. + currentWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST)); - // Reload the current WebView. - currentWebView.reload(); + // Update the menu checkbox. + menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST)); - // Consume the event. - return true; + // Reload the current WebView. + currentWebView.reload(); - case R.id.user_agent_custom: - // Update the user agent. - currentWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value))); + // Consume the event. + return true; + } else if (menuItemId == R.id.ultraprivacy) { // UltraPrivacy. + // Toggle the UltraPrivacy status. + currentWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY)); - // Reload the current WebView. - currentWebView.reload(); + // Update the menu checkbox. + menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY)); - // Consume the event. - return true; + // Reload the current WebView. + currentWebView.reload(); - case R.id.font_size: - // Instantiate the font size dialog. - DialogFragment fontSizeDialogFragment = FontSizeDialog.displayDialog(currentWebView.getSettings().getTextZoom()); + // Consume the event. + return true; + } else if (menuItemId == R.id.block_all_third_party_requests) { // Block all third-party requests. + //Toggle the third-party requests blocker status. + currentWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, !currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)); - // Show the font size dialog. - fontSizeDialogFragment.show(getSupportFragmentManager(), getString(R.string.font_size)); + // Update the menu checkbox. + menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)); - // Consume the event. - return true; + // Reload the current WebView. + currentWebView.reload(); - case R.id.swipe_to_refresh: - // Toggle the stored status of swipe to refresh. - currentWebView.setSwipeToRefresh(!currentWebView.getSwipeToRefresh()); + // Consume the event. + return true; + } else if (menuItemId == R.id.proxy_none) { // Proxy - None. + // Update the proxy mode. + proxyMode = ProxyHelper.NONE; - // Get a handle for the swipe refresh layout. - SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); + // Apply the proxy mode. + applyProxy(true); - // Update the swipe refresh layout. - if (currentWebView.getSwipeToRefresh()) { // Swipe to refresh is enabled. - // Only enable the swipe refresh layout if the WebView is scrolled to the top. It is updated every time the scroll changes. - swipeRefreshLayout.setEnabled(currentWebView.getY() == 0); - } else { // Swipe to refresh is disabled. - // Disable the swipe refresh layout. - swipeRefreshLayout.setEnabled(false); - } + // Consume the event. + return true; + } else if (menuItemId == R.id.proxy_tor) { // Proxy - Tor. + // Update the proxy mode. + proxyMode = ProxyHelper.TOR; - // Consume the event. - return true; + // Apply the proxy mode. + applyProxy(true); - case R.id.wide_viewport: - // Toggle the viewport. - currentWebView.getSettings().setUseWideViewPort(!currentWebView.getSettings().getUseWideViewPort()); + // Consume the event. + return true; + } else if (menuItemId == R.id.proxy_i2p) { // Proxy - I2P. + // Update the proxy mode. + proxyMode = ProxyHelper.I2P; - // Consume the event. - return true; + // Apply the proxy mode. + applyProxy(true); - case R.id.display_images: - if (currentWebView.getSettings().getLoadsImagesAutomatically()) { // Images are currently loaded automatically. - // Disable loading of images. - currentWebView.getSettings().setLoadsImagesAutomatically(false); + // Consume the event. + return true; + } else if (menuItemId == R.id.proxy_custom) { // Proxy - Custom. + // Update the proxy mode. + proxyMode = ProxyHelper.CUSTOM; - // Reload the website to remove existing images. - currentWebView.reload(); - } else { // Images are not currently loaded automatically. - // Enable loading of images. Missing images will be loaded without the need for a reload. - currentWebView.getSettings().setLoadsImagesAutomatically(true); - } + // Apply the proxy mode. + applyProxy(true); - // Consume the event. - return true; + // Consume the event. + return true; + } else if (menuItemId == R.id.user_agent_privacy_browser) { // User Agent - Privacy Browser. + // Update the user agent. + currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[0]); - case R.id.dark_webview: - // Check to see if dark WebView is supported by this WebView. - if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { - // Toggle the dark WebView setting. - if (WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON) { // Dark WebView is currently enabled. - // Turn off dark WebView. - WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); - } else { // Dark WebView is currently disabled. - // turn on dark WebView. - WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); - } - } + // Reload the current WebView. + currentWebView.reload(); - // Consume the event. - return true; + // Consume the event. + return true; + } else if (menuItemId == R.id.user_agent_webview_default) { // User Agent - WebView Default. + // Update the user agent. + currentWebView.getSettings().setUserAgentString(""); - case R.id.find_on_page: - // Get a handle for the views. - Toolbar toolbar = findViewById(R.id.toolbar); - LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout); - EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext); + // Reload the current WebView. + currentWebView.reload(); - // Set the minimum height of the find on page linear layout to match the toolbar. - findOnPageLinearLayout.setMinimumHeight(toolbar.getHeight()); + // Consume the event. + return true; + } else if (menuItemId == R.id.user_agent_firefox_on_android) { // User Agent - Firefox on Android. + // Update the user agent. + currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[2]); - // Hide the toolbar. - toolbar.setVisibility(View.GONE); + // Reload the current WebView. + currentWebView.reload(); - // Show the find on page linear layout. - findOnPageLinearLayout.setVisibility(View.VISIBLE); + // Consume the event. + return true; + } else if (menuItemId == R.id.user_agent_chrome_on_android) { // User Agent - Chrome on Android. + // Update the user agent. + currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[3]); - // Display the keyboard. The app must wait 200 ms before running the command to work around a bug in Android. - // http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working - findOnPageEditText.postDelayed(() -> { - // Set the focus on `findOnPageEditText`. - findOnPageEditText.requestFocus(); + // Reload the current WebView. + currentWebView.reload(); - // Get a handle for the input method manager. - InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + // Consume the event. + return true; + } else if (menuItemId == R.id.user_agent_safari_on_ios) { // User Agent - Safari on iOS. + // Update the user agent. + currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[4]); - // Remove the lint warning below that the input method manager might be null. - assert inputMethodManager != null; + // Reload the current WebView. + currentWebView.reload(); - // Display the keyboard. `0` sets no input flags. - inputMethodManager.showSoftInput(findOnPageEditText, 0); - }, 200); + // Consume the event. + return true; + } else if (menuItemId == R.id.user_agent_firefox_on_linux) { // User Agent - Firefox on Linux. + // Update the user agent. + currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[5]); - // Consume the event. - return true; + // Reload the current WebView. + currentWebView.reload(); - case R.id.print: - // Get a print manager instance. - PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE); + // Consume the event. + return true; + } else if (menuItemId == R.id.user_agent_chromium_on_linux) { // User Agent - Chromium on Linux. + // Update the user agent. + currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[6]); - // Remove the lint error below that print manager might be null. - assert printManager != null; + // Reload the current WebView. + currentWebView.reload(); - // Create a print document adapter from the current WebView. - PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter(); + // Consume the event. + return true; + } else if (menuItemId == R.id.user_agent_firefox_on_windows) { // User Agent - Firefox on Windows. + // Update the user agent. + currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[7]); - // Print the document. - printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null); + // Reload the current WebView. + currentWebView.reload(); - // Consume the event. - return true; + // Consume the event. + return true; + } else if (menuItemId == R.id.user_agent_chrome_on_windows) { // User Agent - Chrome on Windows. + // Update the user agent. + currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[8]); - case R.id.save_url: - // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. - new PrepareSaveDialog(this, this, getSupportFragmentManager(), StoragePermissionDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), - currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl()); + // Reload the current WebView. + currentWebView.reload(); - // Consume the event. - return true; + // Consume the event. + return true; + } else if (menuItemId == R.id.user_agent_edge_on_windows) { // User Agent - Edge on Windows. + // Update the user agent. + currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[9]); - case R.id.save_archive: - // Instantiate the save dialog. - DialogFragment saveArchiveFragment = SaveWebpageDialog.saveWebpage(StoragePermissionDialog.SAVE_ARCHIVE, null, null, getString(R.string.webpage_mht), null, - false); + // Reload the current WebView. + currentWebView.reload(); - // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name. - saveArchiveFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); + // Consume the event. + return true; + } else if (menuItemId == R.id.user_agent_internet_explorer_on_windows) { // User Agent - Internet Explorer on Windows. + // Update the user agent. + currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[10]); - // Consume the event. - return true; + // Reload the current WebView. + currentWebView.reload(); - case R.id.save_image: - // Instantiate the save dialog. - DialogFragment saveImageFragment = SaveWebpageDialog.saveWebpage(StoragePermissionDialog.SAVE_IMAGE, null, null, getString(R.string.webpage_png), null, - false); + // Consume the event. + return true; + } else if (menuItemId == R.id.user_agent_safari_on_macos) { // User Agent - Safari on macOS. + // Update the user agent. + currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[11]); - // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name. - saveImageFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); + // Reload the current WebView. + currentWebView.reload(); - // Consume the event. - return true; + // Consume the event. + return true; + } else if (menuItemId == R.id.user_agent_custom) { // User Agent - Custom. + // Update the user agent. + currentWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value))); - case R.id.add_to_homescreen: - // Instantiate the create home screen shortcut dialog. - DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), currentWebView.getUrl(), - currentWebView.getFavoriteOrDefaultIcon()); + // Reload the current WebView. + currentWebView.reload(); - // Show the create home screen shortcut dialog. - createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut)); + // Consume the event. + return true; + } else if (menuItemId == R.id.font_size) { // Font size. + // Instantiate the font size dialog. + DialogFragment fontSizeDialogFragment = FontSizeDialog.displayDialog(currentWebView.getSettings().getTextZoom()); - // Consume the event. - return true; + // Show the font size dialog. + fontSizeDialogFragment.show(getSupportFragmentManager(), getString(R.string.font_size)); - case R.id.view_source: - // Create an intent to launch the view source activity. - Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class); + // Consume the event. + return true; + } else if (menuItemId == R.id.swipe_to_refresh) { // Swipe to refresh. + // Toggle the stored status of swipe to refresh. + currentWebView.setSwipeToRefresh(!currentWebView.getSwipeToRefresh()); - // Add the variables to the intent. - viewSourceIntent.putExtra("user_agent", currentWebView.getSettings().getUserAgentString()); - viewSourceIntent.putExtra("current_url", currentWebView.getUrl()); + // Get a handle for the swipe refresh layout. + SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); - // Make it so. - startActivity(viewSourceIntent); + // Update the swipe refresh layout. + if (currentWebView.getSwipeToRefresh()) { // Swipe to refresh is enabled. + // Only enable the swipe refresh layout if the WebView is scrolled to the top. It is updated every time the scroll changes. + swipeRefreshLayout.setEnabled(currentWebView.getY() == 0); + } else { // Swipe to refresh is disabled. + // Disable the swipe refresh layout. + swipeRefreshLayout.setEnabled(false); + } - // Consume the event. - return true; + // Consume the event. + return true; + } else if (menuItemId == R.id.wide_viewport) { // Wide viewport. + // Toggle the viewport. + currentWebView.getSettings().setUseWideViewPort(!currentWebView.getSettings().getUseWideViewPort()); - case R.id.share_url: - // Setup the share string. - String shareString = currentWebView.getTitle() + " – " + currentWebView.getUrl(); + // Consume the event. + return true; + } else if (menuItemId == R.id.display_images) { // Display images. + // Toggle the displaying of images. + if (currentWebView.getSettings().getLoadsImagesAutomatically()) { // Images are currently loaded automatically. + // Disable loading of images. + currentWebView.getSettings().setLoadsImagesAutomatically(false); - // Create the share intent. - Intent shareIntent = new Intent(Intent.ACTION_SEND); + // Reload the website to remove existing images. + currentWebView.reload(); + } else { // Images are not currently loaded automatically. + // Enable loading of images. Missing images will be loaded without the need for a reload. + currentWebView.getSettings().setLoadsImagesAutomatically(true); + } - // Add the share string to the intent. - shareIntent.putExtra(Intent.EXTRA_TEXT, shareString); + // Consume the event. + return true; + } else if (menuItemId == R.id.dark_webview) { // Dark WebView. + // Check to see if dark WebView is supported by this WebView. + if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { + // Toggle the dark WebView setting. + if (WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON) { // Dark WebView is currently enabled. + // Turn off dark WebView. + WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); + } else { // Dark WebView is currently disabled. + // Turn on dark WebView. + WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); + } + } - // Set the MIME type. - shareIntent.setType("text/plain"); + // Consume the event. + return true; + } else if (menuItemId == R.id.find_on_page) { // Find on page. + // Get a handle for the views. + Toolbar toolbar = findViewById(R.id.toolbar); + LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout); + EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext); - // Set the intent to open in a new task. - shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // Set the minimum height of the find on page linear layout to match the toolbar. + findOnPageLinearLayout.setMinimumHeight(toolbar.getHeight()); - // Make it so. - startActivity(Intent.createChooser(shareIntent, getString(R.string.share_url))); + // Hide the toolbar. + toolbar.setVisibility(View.GONE); - // Consume the event. - return true; + // Show the find on page linear layout. + findOnPageLinearLayout.setVisibility(View.VISIBLE); - case R.id.open_with_app: - // Open the URL with an outside app. - openWithApp(currentWebView.getUrl()); + // Display the keyboard. The app must wait 200 ms before running the command to work around a bug in Android. + // http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working + findOnPageEditText.postDelayed(() -> { + // Set the focus on the find on page edit text. + findOnPageEditText.requestFocus(); - // Consume the event. - return true; + // Get a handle for the input method manager. + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - case R.id.open_with_browser: - // Open the URL with an outside browser. - openWithBrowser(currentWebView.getUrl()); + // Remove the lint warning below that the input method manager might be null. + assert inputMethodManager != null; - // Consume the event. - return true; + // Display the keyboard. `0` sets no input flags. + inputMethodManager.showSoftInput(findOnPageEditText, 0); + }, 200); - case R.id.add_or_edit_domain: - if (currentWebView.getDomainSettingsApplied()) { // Edit the current domain settings. - // Reapply the domain settings on returning to `MainWebViewActivity`. - reapplyDomainSettingsOnRestart = true; - - // Create an intent to launch the domains activity. - Intent domainsIntent = new Intent(this, DomainsActivity.class); - - // Add the extra information to the intent. - domainsIntent.putExtra("load_domain", currentWebView.getDomainSettingsDatabaseId()); - domainsIntent.putExtra("close_on_back", true); - domainsIntent.putExtra("current_url", currentWebView.getUrl()); - - // Get the current certificate. - SslCertificate sslCertificate = currentWebView.getCertificate(); - - // Check to see if the SSL certificate is populated. - if (sslCertificate != null) { - // Extract the certificate to strings. - String issuedToCName = sslCertificate.getIssuedTo().getCName(); - String issuedToOName = sslCertificate.getIssuedTo().getOName(); - String issuedToUName = sslCertificate.getIssuedTo().getUName(); - String issuedByCName = sslCertificate.getIssuedBy().getCName(); - String issuedByOName = sslCertificate.getIssuedBy().getOName(); - String issuedByUName = sslCertificate.getIssuedBy().getUName(); - long startDateLong = sslCertificate.getValidNotBeforeDate().getTime(); - long endDateLong = sslCertificate.getValidNotAfterDate().getTime(); - - // Add the certificate to the intent. - domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName); - domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName); - domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName); - domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName); - domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName); - domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName); - domainsIntent.putExtra("ssl_start_date", startDateLong); - domainsIntent.putExtra("ssl_end_date", endDateLong); - } + // Consume the event. + return true; + } else if (menuItemId == R.id.print) { // Print. + // Get a print manager instance. + PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE); - // Check to see if the current IP addresses have been received. - if (currentWebView.hasCurrentIpAddresses()) { - // Add the current IP addresses to the intent. - domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses()); - } + // Remove the lint error below that print manager might be null. + assert printManager != null; - // Make it so. - startActivity(domainsIntent); - } else { // Add a new domain. - // Apply the new domain settings on returning to `MainWebViewActivity`. - reapplyDomainSettingsOnRestart = true; - - // Get the current domain - Uri currentUri = Uri.parse(currentWebView.getUrl()); - String currentDomain = currentUri.getHost(); - - // 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); - - // Create the domain and store the database ID. - int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain); - - // Create an intent to launch the domains activity. - Intent domainsIntent = new Intent(this, DomainsActivity.class); - - // Add the extra information to the intent. - domainsIntent.putExtra("load_domain", newDomainDatabaseId); - domainsIntent.putExtra("close_on_back", true); - domainsIntent.putExtra("current_url", currentWebView.getUrl()); - - // Get the current certificate. - SslCertificate sslCertificate = currentWebView.getCertificate(); - - // Check to see if the SSL certificate is populated. - if (sslCertificate != null) { - // Extract the certificate to strings. - String issuedToCName = sslCertificate.getIssuedTo().getCName(); - String issuedToOName = sslCertificate.getIssuedTo().getOName(); - String issuedToUName = sslCertificate.getIssuedTo().getUName(); - String issuedByCName = sslCertificate.getIssuedBy().getCName(); - String issuedByOName = sslCertificate.getIssuedBy().getOName(); - String issuedByUName = sslCertificate.getIssuedBy().getUName(); - long startDateLong = sslCertificate.getValidNotBeforeDate().getTime(); - long endDateLong = sslCertificate.getValidNotAfterDate().getTime(); - - // Add the certificate to the intent. - domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName); - domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName); - domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName); - domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName); - domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName); - domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName); - domainsIntent.putExtra("ssl_start_date", startDateLong); - domainsIntent.putExtra("ssl_end_date", endDateLong); - } + // Create a print document adapter from the current WebView. + PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter(); - // Check to see if the current IP addresses have been received. - if (currentWebView.hasCurrentIpAddresses()) { - // Add the current IP addresses to the intent. - domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses()); - } + // Print the document. + printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null); - // Make it so. - startActivity(domainsIntent); - } + // Consume the event. + return true; + } else if (menuItemId == R.id.save_url) { // Save URL. + // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. + new PrepareSaveDialog(this, this, getSupportFragmentManager(), StoragePermissionDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), + currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl()); - // Consume the event. - return true; + // Consume the event. + return true; + } else if (menuItemId == R.id.save_archive) { // Save archive. + // Instantiate the save dialog. + DialogFragment saveArchiveFragment = SaveWebpageDialog.saveWebpage(StoragePermissionDialog.SAVE_ARCHIVE, null, null, getString(R.string.webpage_mht), null, + false); - case R.id.ad_consent: - // Instantiate the ad consent dialog. - DialogFragment adConsentDialogFragment = new AdConsentDialog(); + // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name. + saveArchiveFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); - // Display the ad consent dialog. - adConsentDialogFragment.show(getSupportFragmentManager(), getString(R.string.ad_consent)); + // Consume the event. + return true; + } else if (menuItemId == R.id.save_image) { // Save image. + // Instantiate the save dialog. + DialogFragment saveImageFragment = SaveWebpageDialog.saveWebpage(StoragePermissionDialog.SAVE_IMAGE, null, null, getString(R.string.webpage_png), null, + false); - // Consume the event. - return true; + // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name. + saveImageFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); - default: - // Don't consume the event. - return super.onOptionsItemSelected(menuItem); - } - } + // Consume the event. + return true; + } else if (menuItemId == R.id.add_to_homescreen) { // Add to homescreen. + // Instantiate the create home screen shortcut dialog. + DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), currentWebView.getUrl(), + currentWebView.getFavoriteOrDefaultIcon()); - // removeAllCookies is deprecated, but it is required for API < 21. - @Override - public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) { - // Get the menu item ID. - int menuItemId = menuItem.getItemId(); + // Show the create home screen shortcut dialog. + createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut)); - // Get a handle for the shared preferences. - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + // Consume the event. + return true; + } else if (menuItemId == R.id.view_source) { // View source. + // Create an intent to launch the view source activity. + Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class); - // Run the commands that correspond to the selected menu item. - switch (menuItemId) { - case R.id.clear_and_exit: - // Clear and exit Privacy Browser. - clearAndExit(); - break; + // Add the variables to the intent. + viewSourceIntent.putExtra("user_agent", currentWebView.getSettings().getUserAgentString()); + viewSourceIntent.putExtra("current_url", currentWebView.getUrl()); - case R.id.home: - // Load the homepage. - loadUrl(currentWebView, sharedPreferences.getString("homepage", getString(R.string.homepage_default_value))); - break; + // Make it so. + startActivity(viewSourceIntent); - case R.id.back: - if (currentWebView.canGoBack()) { - // Get the current web back forward list. - WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList(); + // Consume the event. + return true; + } else if (menuItemId == R.id.share_url) { // Share URL. + // Setup the share string. + String shareString = currentWebView.getTitle() + " – " + currentWebView.getUrl(); - // Get the previous entry URL. - String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl(); + // Create the share intent. + Intent shareIntent = new Intent(Intent.ACTION_SEND); - // Apply the domain settings. - applyDomainSettings(currentWebView, previousUrl, false, false, false); + // Add the share string to the intent. + shareIntent.putExtra(Intent.EXTRA_TEXT, shareString); - // Load the previous website in the history. - currentWebView.goBack(); - } - break; + // Set the MIME type. + shareIntent.setType("text/plain"); - case R.id.forward: - if (currentWebView.canGoForward()) { - // Get the current web back forward list. - WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList(); + // Set the intent to open in a new task. + shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - // Get the next entry URL. - String nextUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() + 1).getUrl(); + // Make it so. + startActivity(Intent.createChooser(shareIntent, getString(R.string.share_url))); - // Apply the domain settings. - applyDomainSettings(currentWebView, nextUrl, false, false, false); + // Consume the event. + return true; + } else if (menuItemId == R.id.open_with_app) { // Open with app. + // Open the URL with an outside app. + openWithApp(currentWebView.getUrl()); - // Load the next website in the history. - currentWebView.goForward(); - } - break; + // Consume the event. + return true; + } else if (menuItemId == R.id.open_with_browser) { // Open with browser. + // Open the URL with an outside browser. + openWithBrowser(currentWebView.getUrl()); - case R.id.history: - // Instantiate the URL history dialog. - DialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(currentWebView.getWebViewFragmentId()); + // Consume the event. + return true; + } else if (menuItemId == R.id.add_or_edit_domain) { // Add or edit domain. + // Check if domain settings currently exist. + if (currentWebView.getDomainSettingsApplied()) { // Edit the current domain settings. + // Reapply the domain settings on returning to `MainWebViewActivity`. + reapplyDomainSettingsOnRestart = true; - // Show the URL history dialog. - urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history)); - break; + // Create an intent to launch the domains activity. + Intent domainsIntent = new Intent(this, DomainsActivity.class); - case R.id.open: - // Instantiate the open file dialog. - DialogFragment openDialogFragment = new OpenDialog(); + // Add the extra information to the intent. + domainsIntent.putExtra("load_domain", currentWebView.getDomainSettingsDatabaseId()); + domainsIntent.putExtra("close_on_back", true); + domainsIntent.putExtra("current_url", currentWebView.getUrl()); - // Show the open file dialog. - openDialogFragment.show(getSupportFragmentManager(), getString(R.string.open)); - break; + // Get the current certificate. + SslCertificate sslCertificate = currentWebView.getCertificate(); - case R.id.requests: - // Populate the resource requests. - RequestsActivity.resourceRequests = currentWebView.getResourceRequests(); + // Check to see if the SSL certificate is populated. + if (sslCertificate != null) { + // Extract the certificate to strings. + String issuedToCName = sslCertificate.getIssuedTo().getCName(); + String issuedToOName = sslCertificate.getIssuedTo().getOName(); + String issuedToUName = sslCertificate.getIssuedTo().getUName(); + String issuedByCName = sslCertificate.getIssuedBy().getCName(); + String issuedByOName = sslCertificate.getIssuedBy().getOName(); + String issuedByUName = sslCertificate.getIssuedBy().getUName(); + long startDateLong = sslCertificate.getValidNotBeforeDate().getTime(); + long endDateLong = sslCertificate.getValidNotAfterDate().getTime(); - // Create an intent to launch the Requests activity. - Intent requestsIntent = new Intent(this, RequestsActivity.class); + // Add the certificate to the intent. + domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName); + domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName); + domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName); + domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName); + domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName); + domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName); + domainsIntent.putExtra("ssl_start_date", startDateLong); + domainsIntent.putExtra("ssl_end_date", endDateLong); + } - // Add the block third-party requests status to the intent. - requestsIntent.putExtra("block_all_third_party_requests", currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)); + // Check to see if the current IP addresses have been received. + if (currentWebView.hasCurrentIpAddresses()) { + // Add the current IP addresses to the intent. + domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses()); + } // Make it so. - startActivity(requestsIntent); - break; - - case R.id.downloads: - // Launch the system Download Manager. - Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS); + startActivity(domainsIntent); + } else { // Add a new domain. + // Apply the new domain settings on returning to `MainWebViewActivity`. + reapplyDomainSettingsOnRestart = true; - // Launch as a new task so that Download Manager and Privacy Browser show as separate windows in the recent tasks list. - downloadManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // Get the current domain + Uri currentUri = Uri.parse(currentWebView.getUrl()); + String currentDomain = currentUri.getHost(); - // Make it so. - startActivity(downloadManagerIntent); - break; + // 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); - case R.id.domains: - // Set the flag to reapply the domain settings on restart when returning from Domain Settings. - reapplyDomainSettingsOnRestart = true; + // Create the domain and store the database ID. + int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain); - // Launch the domains activity. + // Create an intent to launch the domains activity. Intent domainsIntent = new Intent(this, DomainsActivity.class); // Add the extra information to the intent. + domainsIntent.putExtra("load_domain", newDomainDatabaseId); + domainsIntent.putExtra("close_on_back", true); domainsIntent.putExtra("current_url", currentWebView.getUrl()); // Get the current certificate. @@ -2049,52 +1844,189 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Make it so. startActivity(domainsIntent); - break; + } - case R.id.settings: - // Set the flag to reapply app settings on restart when returning from Settings. - reapplyAppSettingsOnRestart = true; + // Consume the event. + return true; + } else if (menuItemId == R.id.ad_consent) { // Ad consent. + // Instantiate the ad consent dialog. + DialogFragment adConsentDialogFragment = new AdConsentDialog(); - // Set the flag to reapply the domain settings on restart when returning from Settings. - reapplyDomainSettingsOnRestart = true; + // Display the ad consent dialog. + adConsentDialogFragment.show(getSupportFragmentManager(), getString(R.string.ad_consent)); - // Launch the settings activity. - Intent settingsIntent = new Intent(this, SettingsActivity.class); - startActivity(settingsIntent); - break; + // Consume the event. + return true; + } else { // There is no match with the options menu. Pass the event up to the parent method. + // Don't consume the event. + return super.onOptionsItemSelected(menuItem); + } + } - case R.id.import_export: - // Launch the import/export activity. - Intent importExportIntent = new Intent (this, ImportExportActivity.class); - startActivity(importExportIntent); - break; + // removeAllCookies is deprecated, but it is required for API < 21. + @Override + public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) { + // Get a handle for the shared preferences. + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - case R.id.logcat: - // Launch the logcat activity. - Intent logcatIntent = new Intent(this, LogcatActivity.class); - startActivity(logcatIntent); - break; + // Get the menu item ID. + int menuItemId = menuItem.getItemId(); - case R.id.guide: - // Launch `GuideActivity`. - Intent guideIntent = new Intent(this, GuideActivity.class); - startActivity(guideIntent); - break; + // Run the commands that correspond to the selected menu item. + if (menuItemId == R.id.clear_and_exit) { // Clear and exit. + // Clear and exit Privacy Browser. + clearAndExit(); + } else if (menuItemId == R.id.home) { // Home. + // Load the homepage. + loadUrl(currentWebView, sharedPreferences.getString("homepage", getString(R.string.homepage_default_value))); + } else if (menuItemId == R.id.back) { // Back. + // Check if the WebView can go back. + if (currentWebView.canGoBack()) { + // Get the current web back forward list. + WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList(); + + // Get the previous entry URL. + String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl(); + + // Apply the domain settings. + applyDomainSettings(currentWebView, previousUrl, false, false, false); + + // Load the previous website in the history. + currentWebView.goBack(); + } + } else if (menuItemId == R.id.forward) { // Forward. + // Check if the WebView can go forward. + if (currentWebView.canGoForward()) { + // Get the current web back forward list. + WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList(); - case R.id.about: - // Create an intent to launch the about activity. - Intent aboutIntent = new Intent(this, AboutActivity.class); + // Get the next entry URL. + String nextUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() + 1).getUrl(); - // Create a string array for the blocklist versions. - String[] blocklistVersions = new String[] {easyList.get(0).get(0)[0], easyPrivacy.get(0).get(0)[0], fanboysAnnoyanceList.get(0).get(0)[0], fanboysSocialList.get(0).get(0)[0], - ultraList.get(0).get(0)[0], ultraPrivacy.get(0).get(0)[0]}; + // Apply the domain settings. + applyDomainSettings(currentWebView, nextUrl, false, false, false); - // Add the blocklist versions to the intent. - aboutIntent.putExtra("blocklist_versions", blocklistVersions); + // Load the next website in the history. + currentWebView.goForward(); + } + } else if (menuItemId == R.id.history) { // History. + // Instantiate the URL history dialog. + DialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(currentWebView.getWebViewFragmentId()); - // Make it so. - startActivity(aboutIntent); - break; + // Show the URL history dialog. + urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history)); + } else if (menuItemId == R.id.open) { // Open. + // Instantiate the open file dialog. + DialogFragment openDialogFragment = new OpenDialog(); + + // Show the open file dialog. + openDialogFragment.show(getSupportFragmentManager(), getString(R.string.open)); + } else if (menuItemId == R.id.requests) { // Requests. + // Populate the resource requests. + RequestsActivity.resourceRequests = currentWebView.getResourceRequests(); + + // Create an intent to launch the Requests activity. + Intent requestsIntent = new Intent(this, RequestsActivity.class); + + // Add the block third-party requests status to the intent. + requestsIntent.putExtra("block_all_third_party_requests", currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)); + + // Make it so. + startActivity(requestsIntent); + } else if (menuItemId == R.id.downloads) { // Downloads. + // Launch the system Download Manager. + Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS); + + // Launch as a new task so that Download Manager and Privacy Browser show as separate windows in the recent tasks list. + downloadManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // Make it so. + startActivity(downloadManagerIntent); + } else if (menuItemId == R.id.domains) { // Domains. + // Set the flag to reapply the domain settings on restart when returning from Domain Settings. + reapplyDomainSettingsOnRestart = true; + + // Launch the domains activity. + Intent domainsIntent = new Intent(this, DomainsActivity.class); + + // Add the extra information to the intent. + domainsIntent.putExtra("current_url", currentWebView.getUrl()); + + // Get the current certificate. + SslCertificate sslCertificate = currentWebView.getCertificate(); + + // Check to see if the SSL certificate is populated. + if (sslCertificate != null) { + // Extract the certificate to strings. + String issuedToCName = sslCertificate.getIssuedTo().getCName(); + String issuedToOName = sslCertificate.getIssuedTo().getOName(); + String issuedToUName = sslCertificate.getIssuedTo().getUName(); + String issuedByCName = sslCertificate.getIssuedBy().getCName(); + String issuedByOName = sslCertificate.getIssuedBy().getOName(); + String issuedByUName = sslCertificate.getIssuedBy().getUName(); + long startDateLong = sslCertificate.getValidNotBeforeDate().getTime(); + long endDateLong = sslCertificate.getValidNotAfterDate().getTime(); + + // Add the certificate to the intent. + domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName); + domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName); + domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName); + domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName); + domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName); + domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName); + domainsIntent.putExtra("ssl_start_date", startDateLong); + domainsIntent.putExtra("ssl_end_date", endDateLong); + } + + // Check to see if the current IP addresses have been received. + if (currentWebView.hasCurrentIpAddresses()) { + // Add the current IP addresses to the intent. + domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses()); + } + + // Make it so. + startActivity(domainsIntent); + } else if (menuItemId == R.id.settings) { // Settings. + // Set the flag to reapply app settings on restart when returning from Settings. + reapplyAppSettingsOnRestart = true; + + // Set the flag to reapply the domain settings on restart when returning from Settings. + reapplyDomainSettingsOnRestart = true; + + // Launch the settings activity. + Intent settingsIntent = new Intent(this, SettingsActivity.class); + startActivity(settingsIntent); + } else if (menuItemId == R.id.import_export) { // Import/Export. + // Create an intent to launch the import/export activity. + Intent importExportIntent = new Intent(this, ImportExportActivity.class); + + // Make it so. + startActivity(importExportIntent); + } else if (menuItemId == R.id.logcat) { // Logcat. + // Create an intent to launch the logcat activity. + Intent logcatIntent = new Intent(this, LogcatActivity.class); + + // Make it so. + startActivity(logcatIntent); + } else if (menuItemId == R.id.guide) { // Guide. + // Create an intent to launch the guide activity. + Intent guideIntent = new Intent(this, GuideActivity.class); + + // Make it so. + startActivity(guideIntent); + } else if (menuItemId == R.id.about) { // About + // Create an intent to launch the about activity. + Intent aboutIntent = new Intent(this, AboutActivity.class); + + // Create a string array for the blocklist versions. + String[] blocklistVersions = new String[]{easyList.get(0).get(0)[0], easyPrivacy.get(0).get(0)[0], fanboysAnnoyanceList.get(0).get(0)[0], fanboysSocialList.get(0).get(0)[0], + ultraList.get(0).get(0)[0], ultraPrivacy.get(0).get(0)[0]}; + + // Add the blocklist versions to the intent. + aboutIntent.putExtra("blocklist_versions", blocklistVersions); + + // Make it so. + startActivity(aboutIntent); } // Close the navigation drawer. @@ -2560,13 +2492,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, Bitmap favoriteIconBitmap) { + // Remove the incorrect lint warning below that the dialog fragment might be null. + assert dialogFragment != null; + // Get the dialog. Dialog dialog = dialogFragment.getDialog(); // Remove the incorrect lint warning below that the dialog might be null. assert dialog != null; - // Get handles for the views from `dialogFragment`. + // Get handles for the views from the dialog. EditText editFolderNameEditText = dialog.findViewById(R.id.edit_folder_name_edittext); RadioButton currentFolderIconRadioButton = dialog.findViewById(R.id.edit_folder_current_icon_radiobutton); RadioButton defaultFolderIconRadioButton = dialog.findViewById(R.id.edit_folder_default_icon_radiobutton); @@ -2951,6 +2886,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onApplyNewFontSize(DialogFragment dialogFragment) { + // Remove the incorrect lint warning below that the dialog fragment might be null. + assert dialogFragment != null; + // Get the dialog. Dialog dialog = dialogFragment.getDialog(); @@ -6208,22 +6146,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Reset the requests counters. nestedScrollWebView.resetRequestsCounters(); - // Hide the keyboard. - inputMethodManager.hideSoftInputFromWindow(nestedScrollWebView.getWindowToken(), 0); - // Get the current page position. int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId()); - // Update the URL text bar if the page is currently selected. - if (tabLayout.getSelectedTabPosition() == currentPagePosition) { - // Clear the focus from the URL edit text. - urlEditText.clearFocus(); - + // Update the URL text bar if the page is currently selected and the URL edit text is not currently being edited. + if ((tabLayout.getSelectedTabPosition() == currentPagePosition) && !urlEditText.hasFocus()) { // Display the formatted URL text. urlEditText.setText(url); // Apply text highlighting to `urlTextBox`. highlightUrlText(); + + // Hide the keyboard. + inputMethodManager.hideSoftInputFromWindow(nestedScrollWebView.getWindowToken(), 0); } // Reset the list of host IP addresses. @@ -6483,6 +6418,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } else if (launchingIntentUriData != null){ // The intent contains a URL. // Store the URL. urlToLoadString = launchingIntentUriData.toString(); + + // Reset the intent. This prevents a duplicate tab from being created on a subsequent restart if loading an link from a new intent on restart. + // For example, this prevents a duplicate tab if a link is loaded from the Guide after changing the theme in the guide and then changing the theme again in the main activity. + setIntent(new Intent()); } else if (!url.equals("")) { // The activity has been restarted. // Load the saved URL. urlToLoadString = url;