// Get handles for the navigation menu and the back and forward menu items. The menu is zero-based.
Menu navigationMenu = navigationView.getMenu();
- MenuItem navigationCloseTabMenuItem = navigationMenu.getItem(0);
- MenuItem navigationBackMenuItem = navigationMenu.getItem(3);
- MenuItem navigationForwardMenuItem = navigationMenu.getItem(4);
- MenuItem navigationHistoryMenuItem = navigationMenu.getItem(5);
- MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
+ MenuItem navigationBackMenuItem = navigationMenu.getItem(2);
+ MenuItem navigationForwardMenuItem = navigationMenu.getItem(3);
+ MenuItem navigationHistoryMenuItem = navigationMenu.getItem(4);
+ MenuItem navigationRequestsMenuItem = navigationMenu.getItem(5);
// Initialize the web view pager adapter.
webViewPagerAdapter = new WebViewPagerAdapter(getSupportFragmentManager());
}
// Update the navigation menu items.
- navigationCloseTabMenuItem.setEnabled(tabLayout.getTabCount() > 1);
navigationBackMenuItem.setEnabled(currentWebView.canGoBack());
navigationForwardMenuItem.setEnabled(currentWebView.canGoForward());
navigationHistoryMenuItem.setEnabled((currentWebView.canGoBack() || currentWebView.canGoForward()));
// Run the commands that correspond to the selected menu item.
switch (menuItemId) {
- case R.id.close_tab:
- // Close the current tab.
- closeCurrentTab();
- break;
-
case R.id.clear_and_exit:
- // Close the bookmarks cursor and database.
- bookmarksCursor.close();
- bookmarksDatabaseHelper.close();
-
- // Get the status of the clear everything preference.
- boolean clearEverything = sharedPreferences.getBoolean("clear_everything", true);
-
- // 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;
-
- // 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.getInstance().removeAllCookies(null);
- } else {
- CookieManager.getInstance().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 = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies");
- Process deleteCookiesJournalProcess = runtime.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 = 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 deleteDatabaseProcess = runtime.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 = runtime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data"});
- Process deleteWebDataJournalProcess = runtime.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 from each WebView.
- for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
- // Get the WebView tab fragment.
- WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
-
- // Get the fragment view.
- View fragmentView = webViewTabFragment.getView();
-
- // Only clear the cache if the WebView exists.
- if (fragmentView != null) {
- // Get the nested scroll WebView from the tab fragment.
- NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
-
- // Clear the cache for this WebView.
- nestedScrollWebView.clearCache(true);
- }
- }
-
- // Manually delete the cache directories.
- try {
- // Delete the main cache directory.
- Process deleteCacheProcess = runtime.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 = runtime.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.
- }
- }
-
- // Wipe out each WebView.
- for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
- // Get the WebView tab fragment.
- WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
-
- // Get the fragment view.
- View fragmentView = webViewTabFragment.getView();
-
- // Only wipe out the WebView if it exists.
- if (fragmentView != null) {
- // Get the nested scroll WebView from the tab fragment.
- NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
-
- // Clear SSL certificate preferences for this WebView.
- nestedScrollWebView.clearSslPreferences();
-
- // Clear the back/forward history for this WebView.
- nestedScrollWebView.clearHistory();
-
- // Destroy the internal state of `mainWebView`.
- nestedScrollWebView.destroy();
- }
- }
-
- // Clear the custom headers.
- customHeaders.clear();
-
- // 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 = runtime.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);
+ // Clear and exit Privacy Browser.
+ clearAndExit();
break;
case R.id.home:
webViewPagerAdapter.addPage(newTabNumber, webViewPager);
}
+ public void closeTab(View view) {
+ // Get a handle for the tab layout.
+ TabLayout tabLayout = findViewById(R.id.tablayout);
+
+ // Run the command according to the number of tabs.
+ if (tabLayout.getTabCount() > 1) { // There is more than one tab open.
+ // Close the current tab.
+ closeCurrentTab();
+ } else { // There is only one tab open.
+ clearAndExit();
+ }
+ }
+
private void closeCurrentTab() {
// Get handles for the views.
AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
appBarLayout.setExpanded(true);
}
+ private void clearAndExit() {
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Close the bookmarks cursor and database.
+ bookmarksCursor.close();
+ bookmarksDatabaseHelper.close();
+
+ // Get the status of the clear everything preference.
+ boolean clearEverything = sharedPreferences.getBoolean("clear_everything", true);
+
+ // 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;
+
+ // 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.getInstance().removeAllCookies(null);
+ } else {
+ CookieManager.getInstance().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 = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies");
+ Process deleteCookiesJournalProcess = runtime.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 = 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 deleteDatabaseProcess = runtime.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 = runtime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data"});
+ Process deleteWebDataJournalProcess = runtime.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 from each WebView.
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+ // Get the WebView tab fragment.
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
+
+ // Get the fragment view.
+ View fragmentView = webViewTabFragment.getView();
+
+ // Only clear the cache if the WebView exists.
+ if (fragmentView != null) {
+ // Get the nested scroll WebView from the tab fragment.
+ NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+ // Clear the cache for this WebView.
+ nestedScrollWebView.clearCache(true);
+ }
+ }
+
+ // Manually delete the cache directories.
+ try {
+ // Delete the main cache directory.
+ Process deleteCacheProcess = runtime.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 = runtime.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.
+ }
+ }
+
+ // Wipe out each WebView.
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+ // Get the WebView tab fragment.
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
+
+ // Get the fragment view.
+ View fragmentView = webViewTabFragment.getView();
+
+ // Only wipe out the WebView if it exists.
+ if (fragmentView != null) {
+ // Get the nested scroll WebView from the tab fragment.
+ NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+ // Clear SSL certificate preferences for this WebView.
+ nestedScrollWebView.clearSslPreferences();
+
+ // Clear the back/forward history for this WebView.
+ nestedScrollWebView.clearHistory();
+
+ // Destroy the internal state of `mainWebView`.
+ nestedScrollWebView.destroy();
+ }
+ }
+
+ // Clear the custom headers.
+ customHeaders.clear();
+
+ // 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 = runtime.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);
+ }
+
private void setCurrentWebView(int pageNumber) {
// Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
Menu navigationMenu = navigationView.getMenu();
// Get a handle for the navigation requests menu item. The menu is 0 based.
- MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
+ MenuItem navigationRequestsMenuItem = navigationMenu.getItem(5);
// Create an empty web resource response to be used if the resource request is blocked.
WebResourceResponse emptyWebResourceResponse = new WebResourceResponse("text/plain", "utf8", new ByteArrayInputStream("".getBytes()));
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:id="@+id/close_tab"
- android:title="@string/close_tab"
- android:icon="@drawable/tab_enabled_light"
- android:orderInCategory="10" />
-
<item
android:id="@+id/clear_and_exit"
android:title="@string/clear_and_exit"
android:icon="@drawable/open_with_external_app_enabled_light"
- android:orderInCategory="20" />
+ android:orderInCategory="10" />
<!-- If a group has an id, a line is drawn above it in the navigation view. -->
<group
android:id="@+id/home"
android:title="@string/home"
android:icon="@drawable/home_enabled_light"
- android:orderInCategory="30" />
+ android:orderInCategory="20" />
<item
android:id="@+id/back"
android:title="@string/back"
android:icon="@drawable/back"
- android:orderInCategory="40" />
+ android:orderInCategory="30" />
<item
android:id="@+id/forward"
android:title="@string/forward"
android:icon="@drawable/forward"
- android:orderInCategory="50" />
+ android:orderInCategory="40" />
<item
android:id="@+id/history"
android:title="@string/history"
android:icon="@drawable/history"
- android:orderInCategory="60" />
+ android:orderInCategory="50" />
</group>
<!-- If a group has an id, a line is drawn above it in the navigation view. -->
android:id="@+id/requests"
android:title="@string/requests"
android:icon="@drawable/block_ads_enabled_light"
- android:orderInCategory="70" />
+ android:orderInCategory="60" />
<item
android:id="@+id/downloads"
android:title="@string/downloads"
android:icon="@drawable/downloads_light"
- android:orderInCategory="80" />
+ android:orderInCategory="70" />
</group>
<!-- If a group has an id, a line is drawn above it in the navigation view. -->
android:id="@+id/domains"
android:title="@string/domains"
android:icon="@drawable/domains"
- android:orderInCategory="90" />
+ android:orderInCategory="80" />
<item
android:id="@+id/settings"
android:title="@string/settings"
android:icon="@drawable/settings"
- android:orderInCategory="100" />
+ android:orderInCategory="90" />
<item
android:id="@+id/import_export"
android:title="@string/import_export"
android:icon="@drawable/import_export_light"
- android:orderInCategory="110" />
+ android:orderInCategory="100" />
<item
android:id="@+id/logcat"
android:title="@string/logcat"
android:icon="@drawable/bug"
- android:orderInCategory="120" />
+ android:orderInCategory="110" />
</group>
<!-- If a group has an id, a line is drawn above it in the navigation view. -->
android:id="@+id/guide"
android:title="@string/guide"
android:icon="@drawable/guide"
- android:orderInCategory="130" />
+ android:orderInCategory="120" />
<item
android:id="@+id/about"
android:title="@string/about"
android:icon="@drawable/about_light"
- android:orderInCategory="140" />
+ android:orderInCategory="130" />
</group>
</menu>
\ No newline at end of file