]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/commitdiff
Add controls for `Clear and Exit`. Implements https://redmine.stoutner.com/issues...
authorSoren Stoutner <soren@stoutner.com>
Wed, 31 May 2017 00:16:35 +0000 (17:16 -0700)
committerSoren Stoutner <soren@stoutner.com>
Wed, 31 May 2017 00:16:35 +0000 (17:16 -0700)
21 files changed:
app/src/main/assets/de/about_licenses.html
app/src/main/assets/en/about_licenses.html
app/src/main/assets/en/images/ic_delete_forever.png [new file with mode: 0644]
app/src/main/assets/en/images/ic_donut_small.png [new file with mode: 0644]
app/src/main/assets/es/about_licenses.html
app/src/main/assets/es/about_links.html
app/src/main/assets/it/about_licenses.html
app/src/main/assets/zh-rTW/about_licenses.html
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java
app/src/main/res/drawable/cache_cleared.xml [new file with mode: 0644]
app/src/main/res/drawable/cache_warning.xml [new file with mode: 0644]
app/src/main/res/drawable/clear_everything_disabled.xml [new file with mode: 0644]
app/src/main/res/drawable/clear_everything_enabled.xml [new file with mode: 0644]
app/src/main/res/drawable/cookies_cleared.xml [new file with mode: 0644]
app/src/main/res/drawable/dom_storage_cleared.xml [new file with mode: 0644]
app/src/main/res/drawable/dom_storage_warning.xml [new file with mode: 0644]
app/src/main/res/drawable/form_data_cleared.xml [new file with mode: 0644]
app/src/main/res/drawable/form_data_warning.xml [new file with mode: 0644]
app/src/main/res/values/strings.xml
app/src/main/res/xml/preferences.xml

index 8f99162a2cd5314e9c4645c104986ce219eb02ba..f26e12fbb85187a262c100f86f442deaa0fb5ae3 100644 (file)
@@ -79,7 +79,9 @@
         <p><img class="icon" src="../en/images/ic_create_new_folder.png"> ic_create_new_folder.</p>
         <p><img class="icon" src="../en/images/ic_devices_other.png"> ic_devices_other.</p>
         <p><img class="icon" src="../en/images/ic_delete.png"> ic_delete.</p>
+        <p><img class="icon" src="../en/images/ic_delete_forever.png"> ic_delete_forever.</p>
         <p><img class="icon" src="../en/images/ic_dns.png"> ic_dns.</p>
+        <p><img class="icon" src="../en/images/ic_donut_small.png"> ic_donut_small.</p>
         <p><img class="icon" src="../en/images/ic_edit.png"> ic_edit.</p>
         <p><img class="icon" src="../en/images/ic_exit_to_app.png"> ic_exit_to_app.</p>
         <p><img class="icon" src="../en/images/ic_expand_less.png"> ic_expand_less.</p>
         <p><img class="icon" src="../en/images/ic_list.png"> ic_list.</p>
         <p><img class="icon" src="../en/images/ic_local_activity.png"> ic_local_activity.</p>
         <p><img class="icon" src="../en/images/ic_location_off.png"> ic_location_off.</p>
+        <p><img class="icon" src="../en/images/ic_map.png"> ic_map.</p>
         <p><img class="icon" src="../en/images/ic_more.png"> ic_more.</p>
         <p><img class="icon" src="../en/images/ic_question_answer.png"> ic_question_answer.</p>
         <p><img class="icon" src="../en/images/ic_refresh.png"> ic_refresh.</p>
index 01ebe5d19bc69ddb54c61f47bcbe1fd069d4a76c..5b858444240936fa74615d399f1e2c0377c3794c 100644 (file)
@@ -73,7 +73,9 @@
         <p><img class="icon" src="images/ic_create_new_folder.png"> ic_create_new_folder.</p>
         <p><img class="icon" src="images/ic_devices_other.png"> ic_devices_other.</p>
         <p><img class="icon" src="images/ic_delete.png"> ic_delete.</p>
+        <p><img class="icon" src="images/ic_delete_forever.png"> ic_delete_forever.</p>
         <p><img class="icon" src="images/ic_dns.png"> ic_dns.</p>
+        <p><img class="icon" src="images/ic_donut_small.png"> ic_donut_small.</p>
         <p><img class="icon" src="images/ic_edit.png"> ic_edit.</p>
         <p><img class="icon" src="images/ic_exit_to_app.png"> ic_exit_to_app.</p>
         <p><img class="icon" src="images/ic_expand_less.png"> ic_expand_less.</p>
@@ -92,6 +94,7 @@
         <p><img class="icon" src="images/ic_list.png"> ic_list.</p>
         <p><img class="icon" src="images/ic_local_activity.png"> ic_local_activity.</p>
         <p><img class="icon" src="images/ic_location_off.png"> ic_location_off.</p>
+        <p><img class="icon" src="images/ic_map.png"> ic_map.</p>
         <p><img class="icon" src="images/ic_more.png"> ic_more.</p>
         <p><img class="icon" src="images/ic_question_answer.png"> ic_question_answer.</p>
         <p><img class="icon" src="images/ic_refresh.png"> ic_refresh.</p>
diff --git a/app/src/main/assets/en/images/ic_delete_forever.png b/app/src/main/assets/en/images/ic_delete_forever.png
new file mode 100644 (file)
index 0000000..a4e87cc
Binary files /dev/null and b/app/src/main/assets/en/images/ic_delete_forever.png differ
diff --git a/app/src/main/assets/en/images/ic_donut_small.png b/app/src/main/assets/en/images/ic_donut_small.png
new file mode 100644 (file)
index 0000000..1ec1ca8
Binary files /dev/null and b/app/src/main/assets/en/images/ic_donut_small.png differ
index 220c15ee96f37fa07041affd2f0fdb51c4b9eca5..726f27405f1ae9a9001f49e556e4eada2412de52 100644 (file)
@@ -77,7 +77,9 @@
         <p><img class="icon" src="../en/images/ic_create_new_folder.png"> ic_create_new_folder.</p>
         <p><img class="icon" src="../en/images/ic_devices_other.png"> ic_devices_other.</p>
         <p><img class="icon" src="../en/images/ic_delete.png"> ic_delete.</p>
+        <p><img class="icon" src="../en/images/ic_delete_forever.png"> ic_delete_forever.</p>
         <p><img class="icon" src="../en/images/ic_dns.png"> ic_dns.</p>
+        <p><img class="icon" src="../en/images/ic_donut_small.png"> ic_donut_small.</p>
         <p><img class="icon" src="../en/images/ic_edit.png"> ic_edit.</p>
         <p><img class="icon" src="../en/images/ic_exit_to_app.png"> ic_exit_to_app.</p>
         <p><img class="icon" src="../en/images/ic_expand_less.png"> ic_expand_less.</p>
@@ -96,6 +98,7 @@
         <p><img class="icon" src="../en/images/ic_list.png"> ic_list.</p>
         <p><img class="icon" src="../en/images/ic_local_activity.png"> ic_local_activity.</p>
         <p><img class="icon" src="../en/images/ic_location_off.png"> ic_location_off.</p>
+        <p><img class="icon" src="../en/images/ic_map.png"> ic_map.</p>
         <p><img class="icon" src="../en/images/ic_more.png"> ic_more.</p>
         <p><img class="icon" src="../en/images/ic_question_answer.png"> ic_question_answer.</p>
         <p><img class="icon" src="../en/images/ic_refresh.png"> ic_refresh.</p>
index c35dc2ea6dfd9ab514332ef1a72c4b36134402b9..489d7078ce4c6749f34021bd69b8a0d39e1f8d8e 100644 (file)
@@ -37,7 +37,7 @@
 
         <p><a href="https://www.stoutner.com/category/privacy-browser/"><img class="icon" src="../en/images/ic_chrome_reader_mode_dark_blue.png"></a> <a href="https://www.stoutner.com/category/privacy-browser/">Noticias</a></p>
 
-        <p><a href="https://www.stoutner.com/category/roadmap/"><img class="icon" src="../en/images/ic_map_dark_blue.png"></a> <a href="https://www.stoutner.com/category/roadmap/">Roadmap</a></p>
+        <p><a href="https://www.stoutner.com/category/roadmap/"><img class="icon" src="../en/images/ic_map_dark_blue.png"></a> <a href="https://www.stoutner.com/category/roadmap/">Hoja de ruta</a></p>
 
         <p><a href="https://redmine.stoutner.com/projects/privacy-browser/issues"><img class="icon" src="../en/images/ic_bug_report_dark_blue.png"></a> <a href="https://redmine.stoutner.com/projects/privacy-browser/issues">Seguimiento de errores y petición de funciones</a></p>
 
index 96194b23f0398e150ae318f7872b2146940019ab..585eca4a60ec5c24fc8b633ac38d0c88cf3ab4f0 100644 (file)
@@ -80,7 +80,9 @@
         <p><img class="icon" src="../en/images/ic_create_new_folder.png"> ic_create_new_folder.</p>
         <p><img class="icon" src="../en/images/ic_devices_other.png"> ic_devices_other.</p>
         <p><img class="icon" src="../en/images/ic_delete.png"> ic_delete.</p>
+        <p><img class="icon" src="../en/images/ic_delete_forever.png"> ic_delete_forever.</p>
         <p><img class="icon" src="../en/images/ic_dns.png"> ic_dns.</p>
+        <p><img class="icon" src="../en/images/ic_donut_small.png"> ic_donut_small.</p>
         <p><img class="icon" src="../en/images/ic_edit.png"> ic_edit.</p>
         <p><img class="icon" src="../en/images/ic_exit_to_app.png"> ic_exit_to_app.</p>
         <p><img class="icon" src="../en/images/ic_expand_less.png"> ic_expand_less.</p>
         <p><img class="icon" src="../en/images/ic_list.png"> ic_list.</p>
         <p><img class="icon" src="../en/images/ic_local_activity.png"> ic_local_activity.</p>
         <p><img class="icon" src="../en/images/ic_location_off.png"> ic_location_off.</p>
+        <p><img class="icon" src="../en/images/ic_map.png"> ic_map.</p>
         <p><img class="icon" src="../en/images/ic_more.png"> ic_more.</p>
         <p><img class="icon" src="../en/images/ic_question_answer.png"> ic_question_answer.</p>
         <p><img class="icon" src="../en/images/ic_refresh.png"> ic_refresh.</p>
index 9b32ce200b6502ba00ad987087b16e8d2acb0365..5226d7334477bf4df7ca52ea065df138e20a2a06 100644 (file)
@@ -73,7 +73,9 @@
         <p><img class="icon" src="../en/images/ic_create_new_folder.png"> ic_create_new_folder.</p>
         <p><img class="icon" src="../en/images/ic_devices_other.png"> ic_devices_other.</p>
         <p><img class="icon" src="../en/images/ic_delete.png"> ic_delete.</p>
+        <p><img class="icon" src="../en/images/ic_delete_forever.png"> ic_delete_forever.</p>
         <p><img class="icon" src="../en/images/ic_dns.png"> ic_dns.</p>
+        <p><img class="icon" src="../en/images/ic_donut_small.png"> ic_donut_small.</p>
         <p><img class="icon" src="../en/images/ic_edit.png"> ic_edit.</p>
         <p><img class="icon" src="../en/images/ic_exit_to_app.png"> ic_exit_to_app.</p>
         <p><img class="icon" src="../en/images/ic_expand_less.png"> ic_expand_less.</p>
@@ -92,6 +94,7 @@
         <p><img class="icon" src="../en/images/ic_list.png"> ic_list.</p>
         <p><img class="icon" src="../en/images/ic_local_activity.png"> ic_local_activity.</p>
         <p><img class="icon" src="../en/images/ic_location_off.png"> ic_location_off.</p>
+        <p><img class="icon" src="../en/images/ic_map.png"> ic_map.</p>
         <p><img class="icon" src="../en/images/ic_more.png"> ic_more.</p>
         <p><img class="icon" src="../en/images/ic_question_answer.png"> ic_question_answer.</p>
         <p><img class="icon" src="../en/images/ic_refresh.png"> ic_refresh.</p>
index f21cae48e51812b8f606a520d45bd2f0049ccb2b..63617b699cc56db36616b01ad1bdc0ff531f8eb7 100644 (file)
@@ -1547,30 +1547,83 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 break;
 
             case R.id.clearAndExit:
-                // Clear cookies.  The commands changed slightly in API 21.
-                if (Build.VERSION.SDK_INT >= 21) {
-                    cookieManager.removeAllCookies(null);
-                } else {
-                    cookieManager.removeAllCookie();
+                // Get a handle for `sharedPreferences`.  `this` references the current context.
+                SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+                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 {
+                        // We have to use two commands because `Runtime.exec()` does not like `*`.
+                        privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies");
+                        privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies-journal");
+                    } catch (IOException e) {
+                        // Do nothing if an error is thrown.
+                    }
                 }
 
                 // Clear DOM storage.
-                WebStorage domStorage = WebStorage.getInstance();
-                domStorage.deleteAllData();
+                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 directory, as `WebStorage` sometimes will not flush its changes to disk before `System.exit(0)` is run.
+                    try {
+                        // We have to use a `String[]` because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise.
+                        privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
+                    } catch (IOException e) {
+                        // Do nothing if an error is thrown.
+                    }
+                }
 
                 // Clear form data.
-                WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(this);
-                webViewDatabase.clearFormData();
+                if (clearEverything || sharedPreferences.getBoolean("clear_form_data", true)) {
+                    WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(this);
+                    webViewDatabase.clearFormData();
 
-                // Clear the cache.  `true` includes disk files.
-                mainWebView.clearCache(true);
+                    // Manually delete the form data database, as `WebViewDatabase` sometimes will not flush its changes to disk before `System.exit(0)` is run.
+                    try {
+                        // We have to use a `String[]` because the database contains a space and `Runtime.exec` will not escape the string correctly otherwise.
+                        privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data"});
+                        privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data-journal"});
+                    } catch (IOException e) {
+                        // Do nothing if an error is thrown.
+                    }
+                }
 
-                // Clear the back/forward history.
-                mainWebView.clearHistory();
+                // Clear the cache.
+                if (clearEverything || sharedPreferences.getBoolean("clear_cache", true)) {
+                    // `true` includes disk files.
+                    mainWebView.clearCache(true);
+
+                    // Manually delete the cache directories.
+                    try {
+                        // Delete the main cache directory.
+                        privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/cache");
+
+                        // Delete the secondary `Service Worker` cache directory.  We have to use a `String[]` because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise.
+                        privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"});
+                    } catch (IOException e) {
+                        // Do nothing if an error is thrown.
+                    }
+                }
 
-                // Clear any SSL certificate preferences.
+                // Clear SSL certificate preferences.
                 mainWebView.clearSslPreferences();
 
+                // Clear the back/forward history.
+                mainWebView.clearHistory();
+
                 // Clear `formattedUrlString`.
                 formattedUrlString = null;
 
@@ -1583,15 +1636,14 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 // Destroy the internal state of `mainWebView`.
                 mainWebView.destroy();
 
-                // Manually delete cache folders.
-                try {
-                    // Delete the main `cache` folder.
-                    privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/cache");
-
-                    // Delete the `app_webview` folder, which contains an additional `WebView` cache.  See `https://code.google.com/p/android/issues/detail?id=233826&thanks=233826&ts=1486670530`.
-                    privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview");
-                } catch (IOException e) {
-                    // Do nothing if an error is thrown.
+                // 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 {
+                        privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview");
+                    } catch (IOException e) {
+                        // Do nothing if an error is thrown.
+                    }
                 }
 
                 // Close Privacy Browser.  `finishAndRemoveTask` also removes Privacy Browser from the recent app list.
@@ -1604,9 +1656,6 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 // Remove the terminated program from RAM.  The status code is `0`.
                 System.exit(0);
                 break;
-
-            default:
-                break;
         }
 
         // Close the navigation drawer.
@@ -2073,7 +2122,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
     }
 
     private void applyAppSettings() {
-        // Get the shared preference values.  `this` references the current context.
+        // Get a handle for `sharedPreferences`.  `this` references the current context.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
         // Store the values from `sharedPreferences` in variables.
index 2ca684613699fcc570273af45773c09c2fca5014..6d317f06761e517e5cf5f413b518e95e5f157709 100644 (file)
@@ -63,6 +63,11 @@ public class SettingsFragment extends PreferenceFragment {
         final Preference fullScreenBrowsingModePreference = findPreference("full_screen_browsing_mode");
         final Preference hideSystemBarsPreference = findPreference("hide_system_bars");
         final Preference translucentNavigationBarPreference = findPreference("translucent_navigation_bar");
+        final Preference clearEverythingPreference = findPreference("clear_everything");
+        final Preference clearCookiesPreference = findPreference("clear_cookies");
+        final Preference clearDomStoragePreference = findPreference("clear_dom_storage");
+        final Preference clearFormDataPreference = findPreference("clear_form_data");
+        final Preference clearCachePreference = findPreference("clear_cache");
         final Preference homepagePreference = findPreference("homepage");
         final Preference defaultFontSizePreference = findPreference("default_font_size");
         final Preference swipeToRefreshPreference = findPreference("swipe_to_refresh");
@@ -86,6 +91,7 @@ public class SettingsFragment extends PreferenceFragment {
         boolean proxyThroughOrbotBoolean = savedPreferences.getBoolean("proxy_through_orbot", false);
         boolean fullScreenBrowsingModeBoolean = savedPreferences.getBoolean("full_screen_browsing_mode", false);
         boolean hideSystemBarsBoolean = savedPreferences.getBoolean("hide_system_bars", false);
+        boolean clearEverythingBoolean = savedPreferences.getBoolean("clear_everything", true);
 
         // Only enable `thirdPartyCookiesPreference` if `firstPartyCookiesEnabledBoolean` is `true` and API >= 21.
         thirdPartyCookiesPreference.setEnabled(firstPartyCookiesEnabledBoolean && (Build.VERSION.SDK_INT >= 21));
@@ -156,6 +162,11 @@ public class SettingsFragment extends PreferenceFragment {
         // Enable `translucentNavigationBarPreference` only if full screen browsing mode is enabled and `hide_system_bars` is disabled.
         translucentNavigationBarPreference.setEnabled(fullScreenBrowsingModeBoolean && !hideSystemBarsBoolean);
 
+        // Set the status of the `Clear and Exit` preferences.
+        clearCookiesPreference.setEnabled(!clearEverythingBoolean);
+        clearDomStoragePreference.setEnabled(!clearEverythingBoolean);
+        clearFormDataPreference.setEnabled(!clearEverythingBoolean);
+        clearCachePreference.setEnabled(!clearEverythingBoolean);
 
         // Set the homepage URL as the summary text for the `Homepage` preference when the preference screen is loaded.  The default is `https://duckduckgo.com`.
         homepagePreference.setSummary(savedPreferences.getString("homepage", "https://duckduckgo.com"));
@@ -295,6 +306,41 @@ public class SettingsFragment extends PreferenceFragment {
             translucentNavigationBarPreference.setIcon(R.drawable.translucent_bar_ghosted);
         }
 
+        // Set the `clearEverythingPreference` icon.
+        if (clearEverythingBoolean) {
+            clearEverythingPreference.setIcon(R.drawable.clear_everything_enabled);
+        } else {
+            clearEverythingPreference.setIcon(R.drawable.clear_everything_disabled);
+        }
+
+        // Set the `clearCookiesPreference` icon.
+        if (clearEverythingBoolean || savedPreferences.getBoolean("clear_cookies", true)) {
+            clearCookiesPreference.setIcon(R.drawable.cookies_cleared);
+        } else {
+            clearCookiesPreference.setIcon(R.drawable.cookies_warning);
+        }
+
+        // Set the `clearDomStoragePreference` icon.
+        if (clearEverythingBoolean || savedPreferences.getBoolean("clear_dom_storage", true)) {
+            clearDomStoragePreference.setIcon(R.drawable.dom_storage_cleared);
+        } else {
+            clearDomStoragePreference.setIcon(R.drawable.dom_storage_warning);
+        }
+
+        // Set the `clearFormDataPreference` icon.
+        if (clearEverythingBoolean || savedPreferences.getBoolean("clear_form_data", true)) {
+            clearFormDataPreference.setIcon(R.drawable.form_data_cleared);
+        } else {
+            clearFormDataPreference.setIcon(R.drawable.form_data_warning);
+        }
+
+        // Set the `clearCachePreference` icon.
+        if (clearEverythingBoolean || savedPreferences.getBoolean("clear_cache", true)) {
+            clearCachePreference.setIcon(R.drawable.cache_cleared);
+        } else {
+            clearCachePreference.setIcon(R.drawable.cache_warning);
+        }
+
         // Set the `swipeToRefreshPreference` icon.
         if (savedPreferences.getBoolean("swipe_to_refresh", false)) {
             swipeToRefreshPreference.setIcon(R.drawable.refresh_enabled);
@@ -620,6 +666,88 @@ public class SettingsFragment extends PreferenceFragment {
                         }
                         break;
 
+                    case "clear_everything":
+                        // Store the new `clear_everything` status
+                        boolean newClearEverythingBoolean = sharedPreferences.getBoolean("clear_everything", true);
+
+                        // Update the status of the `Clear and Exit` preferences.
+                        clearCookiesPreference.setEnabled(!newClearEverythingBoolean);
+                        clearDomStoragePreference.setEnabled(!newClearEverythingBoolean);
+                        clearFormDataPreference.setEnabled(!newClearEverythingBoolean);
+                        clearCachePreference.setEnabled(!newClearEverythingBoolean);
+
+                        // Update the `clearEverythingPreference` icon.
+                        if (newClearEverythingBoolean) {
+                            clearEverythingPreference.setIcon(R.drawable.clear_everything_enabled);
+                        } else {
+                            clearEverythingPreference.setIcon(R.drawable.clear_everything_disabled);
+                        }
+
+                        // Update the `clearCookiesPreference` icon.
+                        if (newClearEverythingBoolean || sharedPreferences.getBoolean("clear_cookies", true)) {
+                            clearCookiesPreference.setIcon(R.drawable.cookies_cleared);
+                        } else {
+                            clearCookiesPreference.setIcon(R.drawable.cookies_warning);
+                        }
+
+                        // Update the `clearDomStoragePreference` icon.
+                        if (newClearEverythingBoolean || sharedPreferences.getBoolean("clear_dom_storage", true)) {
+                            clearDomStoragePreference.setIcon(R.drawable.dom_storage_cleared);
+                        } else {
+                            clearDomStoragePreference.setIcon(R.drawable.dom_storage_warning);
+                        }
+
+                        // Update the `clearFormDataPreference` icon.
+                        if (newClearEverythingBoolean || sharedPreferences.getBoolean("clear_form_data", true)) {
+                            clearFormDataPreference.setIcon(R.drawable.form_data_cleared);
+                        } else {
+                            clearFormDataPreference.setIcon(R.drawable.form_data_warning);
+                        }
+
+                        // Update the `clearCachePreference` icon.
+                        if (newClearEverythingBoolean || sharedPreferences.getBoolean("clear_cache", true)) {
+                            clearCachePreference.setIcon(R.drawable.cache_cleared);
+                        } else {
+                            clearCachePreference.setIcon(R.drawable.cache_warning);
+                        }
+                        break;
+
+                    case "clear_cookies":
+                        // Update the icon.
+                        if (sharedPreferences.getBoolean("clear_cookies", true)) {
+                            clearCookiesPreference.setIcon(R.drawable.cookies_cleared);
+                        } else {
+                            clearCookiesPreference.setIcon(R.drawable.cookies_warning);
+                        }
+                        break;
+
+                    case "clear_dom_storage":
+                        // Update the icon.
+                        if (sharedPreferences.getBoolean("clear_dom_storage", true)) {
+                            clearDomStoragePreference.setIcon(R.drawable.dom_storage_cleared);
+                        } else {
+                            clearDomStoragePreference.setIcon(R.drawable.dom_storage_warning);
+                        }
+                        break;
+
+                    case "clear_form_data":
+                        // Update the icon.
+                        if (sharedPreferences.getBoolean("clear_form_data", true)) {
+                            clearFormDataPreference.setIcon(R.drawable.form_data_cleared);
+                        } else {
+                            clearFormDataPreference.setIcon(R.drawable.form_data_warning);
+                        }
+                        break;
+
+                    case "clear_cache":
+                        // Update the icon.
+                        if (sharedPreferences.getBoolean("clear_cache", true)) {
+                            clearCachePreference.setIcon(R.drawable.cache_cleared);
+                        } else {
+                            clearCachePreference.setIcon(R.drawable.cache_warning);
+                        }
+                        break;
+
                     case "homepage":
                         // Set the new homepage URL as the summary text for the Homepage preference.  The default is `https://www.duckduckgo.com`.
                         homepagePreference.setSummary(sharedPreferences.getString("homepage", "https://www.duckduckgo.com"));
diff --git a/app/src/main/res/drawable/cache_cleared.xml b/app/src/main/res/drawable/cache_cleared.xml
new file mode 100644 (file)
index 0000000..4edfd87
--- /dev/null
@@ -0,0 +1,18 @@
+<!-- `cache_cleared.xml` comes from the Android Material icon set, where it is called `ic_donut_small`.  It is released under the Apache License 2.0. -->
+
+<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true"` not applying to API < 21. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:autoMirrored="true"
+    android:height="24dp"
+    android:width="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    tools:ignore="VectorRaster" >
+
+    <!-- We have to use a hard coded color code until API >= 21.  Then we can use `@color`. -->
+    <path
+        android:fillColor="#FF1565C0"
+        android:pathData="M11,9.16V2c-5,0.5 -9,4.79 -9,10s4,9.5 9,10v-7.16c-1,-0.41 -2,-1.52 -2,-2.84s1,-2.43 2,-2.84zM14.86,11H22c-0.48,-4.75 -4,-8.53 -9,-9v7.16c1,0.3 1.52,0.98 1.86,1.84zM13,14.84V22c5,-0.47 8.52,-4.25 9,-9h-7.14c-0.34,0.86 -0.86,1.54 -1.86,1.84z"/>
+</vector>
diff --git a/app/src/main/res/drawable/cache_warning.xml b/app/src/main/res/drawable/cache_warning.xml
new file mode 100644 (file)
index 0000000..add2185
--- /dev/null
@@ -0,0 +1,18 @@
+<!-- `cache_cleared.xml` comes from the Android Material icon set, where it is called `ic_donut_small`.  It is released under the Apache License 2.0. -->
+
+<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true"` not applying to API < 21. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:autoMirrored="true"
+    android:height="24dp"
+    android:width="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    tools:ignore="VectorRaster" >
+
+    <!-- We have to use a hard coded color code until API >= 21.  Then we can use `@color`. -->
+    <path
+        android:fillColor="#FFD50000"
+        android:pathData="M11,9.16V2c-5,0.5 -9,4.79 -9,10s4,9.5 9,10v-7.16c-1,-0.41 -2,-1.52 -2,-2.84s1,-2.43 2,-2.84zM14.86,11H22c-0.48,-4.75 -4,-8.53 -9,-9v7.16c1,0.3 1.52,0.98 1.86,1.84zM13,14.84V22c5,-0.47 8.52,-4.25 9,-9h-7.14c-0.34,0.86 -0.86,1.54 -1.86,1.84z"/>
+</vector>
diff --git a/app/src/main/res/drawable/clear_everything_disabled.xml b/app/src/main/res/drawable/clear_everything_disabled.xml
new file mode 100644 (file)
index 0000000..fcf007a
--- /dev/null
@@ -0,0 +1,13 @@
+<!-- `clear_everything_disabled.xml` comes from the Android Material icon set, where it is called `ic_delete_forever`.  It is released under the Apache License 2.0. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:width="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0" >
+
+    <!-- We have to use a hard coded color code until API >= 21.  Then we can use `@color`. -->
+    <path
+        android:fillColor="#FFD50000"
+        android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8.46,11.88l1.41,-1.41L12,12.59l2.12,-2.12 1.41,1.41L13.41,14l2.12,2.12 -1.41,1.41L12,15.41l-2.12,2.12 -1.41,-1.41L10.59,14l-2.13,-2.12zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z"/>
+</vector>
diff --git a/app/src/main/res/drawable/clear_everything_enabled.xml b/app/src/main/res/drawable/clear_everything_enabled.xml
new file mode 100644 (file)
index 0000000..6c901ab
--- /dev/null
@@ -0,0 +1,13 @@
+<!-- `clear_everything_enabled.xml` comes from the Android Material icon set, where it is called `ic_delete_forever`.  It is released under the Apache License 2.0. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:width="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0" >
+
+    <!-- We have to use a hard coded color code until API >= 21.  Then we can use `@color`. -->
+    <path
+        android:fillColor="#FF1565C0"
+        android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8.46,11.88l1.41,-1.41L12,12.59l2.12,-2.12 1.41,1.41L13.41,14l2.12,2.12 -1.41,1.41L12,15.41l-2.12,2.12 -1.41,-1.41L10.59,14l-2.13,-2.12zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z"/>
+</vector>
diff --git a/app/src/main/res/drawable/cookies_cleared.xml b/app/src/main/res/drawable/cookies_cleared.xml
new file mode 100644 (file)
index 0000000..f0ba32b
--- /dev/null
@@ -0,0 +1,18 @@
+<!-- `cookies_cleared.xml` was created by Google and downloaded from <https://materialdesignicons.com/icon/cookie>.  It is released under the Apache License 2.0. -->
+
+<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true"` not applying to API < 21. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:height="26dp"
+    android:width="26dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:autoMirrored="true"
+    tools:ignore="VectorRaster" >
+
+    <!-- We have to use a hard coded color code until API >= 21.  Then we can use `@color`. -->
+    <path
+        android:fillColor="#FF1565C0"
+        android:pathData="M12,3A9,9 0 0,0 3,12A9,9 0 0,0 12,21A9,9 0 0,0 21,12C21,11.5 20.96,11 20.87,10.5C20.6,10 20,10 20,10H18V9C18,8 17,8 17,8H15V7C15,6 14,6 14,6H13V4C13,3 12,3 12,3M9.5,6A1.5,1.5 0 0,1 11,7.5A1.5,1.5 0 0,1 9.5,9A1.5,1.5 0 0,1 8,7.5A1.5,1.5 0 0,1 9.5,6M6.5,10A1.5,1.5 0 0,1 8,11.5A1.5,1.5 0 0,1 6.5,13A1.5,1.5 0 0,1 5,11.5A1.5,1.5 0 0,1 6.5,10M11.5,11A1.5,1.5 0 0,1 13,12.5A1.5,1.5 0 0,1 11.5,14A1.5,1.5 0 0,1 10,12.5A1.5,1.5 0 0,1 11.5,11M16.5,13A1.5,1.5 0 0,1 18,14.5A1.5,1.5 0 0,1 16.5,16H16.5A1.5,1.5 0 0,1 15,14.5H15A1.5,1.5 0 0,1 16.5,13M11,16A1.5,1.5 0 0,1 12.5,17.5A1.5,1.5 0 0,1 11,19A1.5,1.5 0 0,1 9.5,17.5A1.5,1.5 0 0,1 11,16Z" />
+</vector>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/dom_storage_cleared.xml b/app/src/main/res/drawable/dom_storage_cleared.xml
new file mode 100644 (file)
index 0000000..a20c8ca
--- /dev/null
@@ -0,0 +1,18 @@
+<!-- `dom_storage_cleared.xml` comes from the Android Material icon set, where it is called `ic_web`.  It is released under the Apache License 2.0. -->
+
+<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true"` not applying to API < 21. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:height="26dp"
+    android:width="26dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:autoMirrored="true"
+    tools:ignore="VectorRaster" >
+
+    <!-- We have to use a hard coded color code until API >= 21.  Then we can use `@color`. -->
+    <path
+        android:fillColor="#FF1565C0"
+        android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM15,18L4,18v-4h11v4zM15,13L4,13L4,9h11v4zM20,18h-4L16,9h4v9z"/>
+</vector>
diff --git a/app/src/main/res/drawable/dom_storage_warning.xml b/app/src/main/res/drawable/dom_storage_warning.xml
new file mode 100644 (file)
index 0000000..ee18ccf
--- /dev/null
@@ -0,0 +1,18 @@
+<!-- `dom_storage_cleared.xml` comes from the Android Material icon set, where it is called `ic_web`.  It is released under the Apache License 2.0. -->
+
+<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true"` not applying to API < 21. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:height="26dp"
+    android:width="26dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:autoMirrored="true"
+    tools:ignore="VectorRaster" >
+
+    <!-- We have to use a hard coded color code until API >= 21.  Then we can use `@color`. -->
+    <path
+        android:fillColor="#FFD50000"
+        android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM15,18L4,18v-4h11v4zM15,13L4,13L4,9h11v4zM20,18h-4L16,9h4v9z"/>
+</vector>
diff --git a/app/src/main/res/drawable/form_data_cleared.xml b/app/src/main/res/drawable/form_data_cleared.xml
new file mode 100644 (file)
index 0000000..4437a3e
--- /dev/null
@@ -0,0 +1,18 @@
+<!-- `form_data_cleared.xml` comes from the Android Material icon set, where it is called `ic_subtitles`.  It is released under the Apache License 2.0. -->
+
+<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true"` not applying to API < 21. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:height="26dp"
+    android:width="26dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:autoMirrored="true"
+    tools:ignore="VectorRaster">
+
+    <!-- We have to use a hard coded color code until API >= 21.  Then we can use `@color`. -->
+    <path
+        android:fillColor="#FF1565C0"
+        android:pathData="M20,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM4,12h4v2L4,14v-2zM14,18L4,18v-2h10v2zM20,18h-4v-2h4v2zM20,14L10,14v-2h10v2z"/>
+</vector>
diff --git a/app/src/main/res/drawable/form_data_warning.xml b/app/src/main/res/drawable/form_data_warning.xml
new file mode 100644 (file)
index 0000000..71d77a7
--- /dev/null
@@ -0,0 +1,18 @@
+<!-- `form_data_warning.xml` comes from the Android Material icon set, where it is called `ic_subtitles`.  It is released under the Apache License 2.0. -->
+
+<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true"` not applying to API < 21. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:height="26dp"
+    android:width="26dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:autoMirrored="true"
+    tools:ignore="VectorRaster">
+
+    <!-- We have to use a hard coded color code until API >= 21.  Then we can use `@color`. -->
+    <path
+        android:fillColor="#FFD50000"
+        android:pathData="M20,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM4,12h4v2L4,14v-2zM14,18L4,18v-2h10v2zM20,18h-4v-2h4v2zM20,14L10,14v-2h10v2z"/>
+</vector>
index 5d1b86ae29a96504c69df31c38d79cc47e6a9f74..d8eb744dd23e9d3db10c9a90174b5758fd6836b5 100644 (file)
         <string name="hide_system_bars_summary">Hide the status and navigation bars in full screen browsing mode. This doesn’t work well if the keyboard is displayed during full screen browsing mode.</string>
         <string name="translucent_navigation_bar">Translucent navigation bar</string>
         <string name="translucent_navigation_bar_summary">Make the navigation bar translucent in full screen browsing mode.</string>
+    <string name="clear_everything">Clear everything</string>
+    <string name="clear_everything_summary">Clear cookies, DOM storage, form data, and the cache.  Then manually delete the entire “app_webview” and “cache” directories.</string>
+    <string name="clear_cookies_preference">Clear cookies</string>
+    <string name="clear_cookies_summary">Clears first and third-party cookies.</string>
+    <string name="clear_dom_storage_preference">Clear DOM storage</string>
+    <string name="clear_dom_storage_summary">Clear DOM storage.</string>
+    <string name="clear_form_data_preference">Clear form data</string>
+    <string name="clear_form_data_summary">Clears form data.</string>
+    <string name="clear_cache">Clear cache</string>
+    <string name="clear_cache_summary">Clear WebView’s cache.</string>
     <string name="general">General</string>
         <string name="homepage">Homepage</string>
         <string name="default_font_size">Default font size</string>
index 87b2eac2a6a92c529a219c8256476d090f40274f..162d932bcfa80196492dbdb979b4cafc4df74efd 100644 (file)
 
     <PreferenceCategory
         android:key="search_category"
-        android:title="@string/search">
+        android:title="@string/search" >
 
         <ListPreference
             android:key="search"
 
     <PreferenceCategory
         android:key="full_screen"
-        android:title="@string/full_screen">
+        android:title="@string/full_screen" >
 
         <SwitchPreference
             android:key="full_screen_browsing_mode"
             android:defaultValue="true" />
     </PreferenceCategory>
 
+    <PreferenceCategory
+        android:key="clean_and_exit"
+        android:title="@string/clear_and_exit" >
+
+        <SwitchPreference
+            android:key="clear_everything"
+            android:title="@string/clear_everything"
+            android:summary="@string/clear_everything_summary"
+            android:defaultValue="true" />
+
+        <SwitchPreference
+            android:key="clear_cookies"
+            android:title="@string/clear_cookies_preference"
+            android:summary="@string/clear_cookies_summary"
+            android:defaultValue="true" />
+
+        <SwitchPreference
+            android:key="clear_dom_storage"
+            android:title="@string/clear_dom_storage_preference"
+            android:summary="@string/clear_dom_storage_summary"
+            android:defaultValue="true" />
+
+        <SwitchPreference
+            android:key="clear_form_data"
+            android:title="@string/clear_form_data_preference"
+            android:summary="@string/clear_form_data_summary"
+            android:defaultValue="true" />
+
+        <SwitchPreference
+            android:key="clear_cache"
+            android:title="@string/clear_cache"
+            android:summary="@string/clear_cache_summary"
+            android:defaultValue="true" />
+    </PreferenceCategory>
+
     <PreferenceCategory
         android:key="general"
         android:title="@string/general" >