+ }
+
+ // removeAllCookies is deprecated, but it is required for API < 21.
+ @SuppressWarnings("deprecation")
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
+ // Get the menu item ID.
+ int menuItemId = menuItem.getItemId();
+
+ // Run the commands that correspond to the selected menu item.
+ switch (menuItemId) {
+ case R.id.close_tab:
+ // Get a handle for the tab layout.
+ TabLayout tabLayout = findViewById(R.id.tablayout);
+
+ // Get the current tab number.
+ int currentTabNumber = tabLayout.getSelectedTabPosition();
+
+ // Delete the tab and page.
+ webViewPagerAdapter.deletePage(currentTabNumber);
+ break;
+
+ case R.id.clear_and_exit:
+ // Close the bookmarks cursor and database.
+ bookmarksCursor.close();
+ bookmarksDatabaseHelper.close();
+
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Get the status of the clear everything preference.
+ boolean clearEverything = sharedPreferences.getBoolean("clear_everything", true);
+
+ // Clear cookies.
+ if (clearEverything || sharedPreferences.getBoolean("clear_cookies", true)) {
+ // The command to remove cookies changed slightly in API 21.
+ if (Build.VERSION.SDK_INT >= 21) {
+ cookieManager.removeAllCookies(null);
+ } else {
+ cookieManager.removeAllCookie();
+ }
+
+ // Manually delete the cookies database, as `CookieManager` sometimes will not flush its changes to disk before `System.exit(0)` is run.
+ try {
+ // Two commands must be used because `Runtime.exec()` does not like `*`.
+ Process deleteCookiesProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies");
+ Process deleteCookiesJournalProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies-journal");
+
+ // Wait until the processes have finished.
+ deleteCookiesProcess.waitFor();
+ deleteCookiesJournalProcess.waitFor();
+ } catch (Exception exception) {
+ // Do nothing if an error is thrown.
+ }
+ }
+
+ // Clear DOM storage.
+ if (clearEverything || sharedPreferences.getBoolean("clear_dom_storage", true)) {
+ // Ask `WebStorage` to clear the DOM storage.
+ WebStorage webStorage = WebStorage.getInstance();
+ webStorage.deleteAllData();
+
+ // Manually delete the DOM storage files and directories, as `WebStorage` sometimes will not flush its changes to disk before `System.exit(0)` is run.
+ try {
+ // A `String[]` must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
+ Process deleteLocalStorageProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
+
+ // Multiple commands must be used because `Runtime.exec()` does not like `*`.
+ Process deleteIndexProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
+ Process deleteQuotaManagerProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
+ Process deleteQuotaManagerJournalProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
+ Process deleteDatabaseProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
+
+ // Wait until the processes have finished.
+ deleteLocalStorageProcess.waitFor();
+ deleteIndexProcess.waitFor();
+ deleteQuotaManagerProcess.waitFor();
+ deleteQuotaManagerJournalProcess.waitFor();
+ deleteDatabaseProcess.waitFor();
+ } catch (Exception exception) {
+ // Do nothing if an error is thrown.
+ }
+ }
+
+ // Clear form data if the API < 26.
+ if ((Build.VERSION.SDK_INT < 26) && (clearEverything || sharedPreferences.getBoolean("clear_form_data", true))) {
+ WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(this);
+ webViewDatabase.clearFormData();
+
+ // Manually delete the form data database, as `WebViewDatabase` sometimes will not flush its changes to disk before `System.exit(0)` is run.
+ try {
+ // A string array must be used because the database contains a space and `Runtime.exec` will not otherwise escape the string correctly.
+ Process deleteWebDataProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data"});
+ Process deleteWebDataJournalProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data-journal"});
+
+ // Wait until the processes have finished.
+ deleteWebDataProcess.waitFor();
+ deleteWebDataJournalProcess.waitFor();
+ } catch (Exception exception) {
+ // Do nothing if an error is thrown.
+ }
+ }
+
+ // Clear the cache.
+ if (clearEverything || sharedPreferences.getBoolean("clear_cache", true)) {
+ // Clear the cache.
+ // TODO
+ currentWebView.clearCache(true);
+
+ // Manually delete the cache directories.
+ try {
+ // Delete the main cache directory.
+ Process deleteCacheProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/cache");
+
+ // Delete the secondary `Service Worker` cache directory.
+ // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
+ Process deleteServiceWorkerProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"});
+
+ // Wait until the processes have finished.
+ deleteCacheProcess.waitFor();
+ deleteServiceWorkerProcess.waitFor();
+ } catch (Exception exception) {
+ // Do nothing if an error is thrown.
+ }
+ }
+
+ // Clear SSL certificate preferences.
+ // TODO
+ currentWebView.clearSslPreferences();
+
+ // Clear the back/forward history.
+ // TODO
+ currentWebView.clearHistory();
+
+ // Clear `formattedUrlString`.
+ formattedUrlString = null;
+
+ // Clear `customHeaders`.
+ customHeaders.clear();
+
+ // Destroy the internal state of `mainWebView`.
+ // TODO
+ currentWebView.destroy();
+
+ // Manually delete the `app_webview` folder, which contains the cookies, DOM storage, form data, and `Service Worker` cache.
+ // See `https://code.google.com/p/android/issues/detail?id=233826&thanks=233826&ts=1486670530`.
+ if (clearEverything) {
+ try {
+ // Delete the folder.
+ Process deleteAppWebviewProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview");
+
+ // Wait until the process has finished.
+ deleteAppWebviewProcess.waitFor();
+ } catch (Exception exception) {
+ // Do nothing if an error is thrown.
+ }
+ }
+
+ // Close Privacy Browser. `finishAndRemoveTask` also removes Privacy Browser from the recent app list.
+ if (Build.VERSION.SDK_INT >= 21) {
+ finishAndRemoveTask();
+ } else {
+ finish();
+ }
+
+ // Remove the terminated program from RAM. The status code is `0`.
+ System.exit(0);
+ break;
+
+ case R.id.home:
+ loadUrl(homepage);
+ break;
+
+ case R.id.back:
+ if (currentWebView.canGoBack()) {
+ // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+ formattedUrlString = "";
+
+ // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
+ navigatingHistory = true;
+
+ // Load the previous website in the history.
+ currentWebView.goBack();
+ }
+ break;
+
+ case R.id.forward:
+ if (currentWebView.canGoForward()) {
+ // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+ formattedUrlString = "";
+
+ // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
+ navigatingHistory = true;
+
+ // Load the next website in the history.
+ currentWebView.goForward();
+ }
+ break;
+
+ case R.id.history:
+ // Get the `WebBackForwardList`.
+ WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
+
+ // Show the URL history dialog and name this instance `R.string.history`.
+ DialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(this, webBackForwardList);
+ urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history));
+ break;
+
+ case R.id.requests:
+ // Launch the requests activity.
+ Intent requestsIntent = new Intent(this, RequestsActivity.class);
+ startActivity(requestsIntent);
+ break;
+
+ case R.id.downloads:
+ // Launch the system Download Manager.
+ Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
+
+ // 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);
+
+ startActivity(downloadManagerIntent);
+ break;