]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java
Block trackers listed at privacytests.org. https://redmine.stoutner.com/issues/863
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / fragments / SettingsFragment.java
1 /*
2  * Copyright © 2016-2022 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
5  *
6  * Privacy Browser Android is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Privacy Browser Android is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package com.stoutner.privacybrowser.fragments;
21
22 import android.annotation.SuppressLint;
23 import android.app.Activity;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.SharedPreferences;
27 import android.content.res.Configuration;
28 import android.content.res.Resources;
29 import android.os.Build;
30 import android.os.Bundle;
31 import android.os.Handler;
32 import android.os.Looper;
33 import android.view.LayoutInflater;
34 import android.view.View;
35 import android.webkit.WebView;
36 import android.widget.ArrayAdapter;
37
38 import androidx.appcompat.app.AppCompatDelegate;
39 import androidx.preference.Preference;
40 import androidx.preference.PreferenceCategory;
41 import androidx.preference.PreferenceFragmentCompat;
42
43 import com.stoutner.privacybrowser.R;
44 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
45 import com.stoutner.privacybrowser.helpers.ProxyHelper;
46
47 import java.util.Objects;
48
49 public class SettingsFragment extends PreferenceFragmentCompat {
50     // Declare the class variables.
51     private int currentThemeStatus;
52     private String defaultUserAgent;
53     private ArrayAdapter<CharSequence> userAgentNamesArray;
54     private String[] translatedUserAgentNamesArray;
55     private String[] userAgentDataArray;
56     private String[] appThemeEntriesStringArray;
57     private String[] appThemeEntryValuesStringArray;
58     private String[] webViewThemeEntriesStringArray;
59     private String[] webViewThemeEntryValuesStringArray;
60     private SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener;
61
62     // Declare the class views.
63     private Preference javaScriptPreference;
64     private Preference cookiesPreference;
65     private Preference domStoragePreference;
66     private Preference formDataPreference;  // The form data preference can be removed once the minimum API >= 26.
67     private Preference userAgentPreference;
68     private Preference customUserAgentPreference;
69     private Preference incognitoModePreference;
70     private Preference allowScreenshotsPreference;
71     private Preference easyListPreference;
72     private Preference easyPrivacyPreference;
73     private Preference fanboyAnnoyanceListPreference;
74     private Preference fanboySocialBlockingListPreference;
75     private Preference ultraListPreference;
76     private Preference ultraPrivacyPreference;
77     private Preference blockAllThirdPartyRequestsPreference;
78     private Preference trackingQueriesPreference;
79     private Preference ampRedirectsPreference;
80     private Preference searchPreference;
81     private Preference searchCustomURLPreference;
82     private Preference proxyPreference;
83     private Preference proxyCustomUrlPreference;
84     private Preference fullScreenBrowsingModePreference;
85     private Preference hideAppBarPreference;
86     private Preference clearEverythingPreference;
87     private Preference clearCookiesPreference;
88     private Preference clearDomStoragePreference;
89     private Preference clearFormDataPreference;  // The clear form data preference can be removed once the minimum API >= 26.
90     private Preference clearLogcatPreference;
91     private Preference clearCachePreference;
92     private Preference homepagePreference;
93     private Preference fontSizePreference;
94     private Preference openIntentsInNewTabPreference;
95     private Preference swipeToRefreshPreference;
96     private Preference downloadWithExternalAppPreference;
97     private Preference scrollAppBarPreference;
98     private Preference bottomAppBarPreference;
99     private Preference displayAdditionalAppBarIconsPreference;
100     private Preference appThemePreference;
101     private Preference webViewThemePreference;
102     private Preference wideViewportPreference;
103     private Preference displayWebpageImagesPreference;
104
105     @Override
106     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
107         // Load the preferences from the XML file.
108         setPreferencesFromResource(R.xml.preferences, rootKey);
109
110         // Get a handle for the activity.
111         Activity activity = getActivity();
112
113         // Remove the lint warning below that `getApplicationContext()` might produce a null pointer exception.
114         assert activity != null;
115
116         // Get a handle for the resources.
117         Resources resources = getResources();
118
119         // Get the current theme status.
120         currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
121
122         // Get a handle for the shared preferences.
123         SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();
124
125         // Remove the incorrect warning below that the shared preferences might be null.
126         assert sharedPreferences != null;
127
128         // Get handles for the preferences.
129         javaScriptPreference = findPreference("javascript");
130         cookiesPreference = findPreference(getString(R.string.cookies_key));
131         domStoragePreference = findPreference("dom_storage");
132         formDataPreference = findPreference("save_form_data");  // The form data preference can be removed once the minimum API >= 26.
133         userAgentPreference = findPreference("user_agent");
134         customUserAgentPreference = findPreference("custom_user_agent");
135         incognitoModePreference = findPreference("incognito_mode");
136         allowScreenshotsPreference = findPreference(getString(R.string.allow_screenshots_key));
137         easyListPreference = findPreference("easylist");
138         easyPrivacyPreference = findPreference("easyprivacy");
139         fanboyAnnoyanceListPreference = findPreference("fanboys_annoyance_list");
140         fanboySocialBlockingListPreference = findPreference("fanboys_social_blocking_list");
141         ultraListPreference = findPreference("ultralist");
142         ultraPrivacyPreference = findPreference("ultraprivacy");
143         blockAllThirdPartyRequestsPreference = findPreference("block_all_third_party_requests");
144         trackingQueriesPreference = findPreference(getString(R.string.tracking_queries_key));
145         ampRedirectsPreference = findPreference(getString(R.string.amp_redirects_key));
146         searchPreference = findPreference("search");
147         searchCustomURLPreference = findPreference("search_custom_url");
148         proxyPreference = findPreference("proxy");
149         proxyCustomUrlPreference = findPreference(getString(R.string.proxy_custom_url_key));
150         fullScreenBrowsingModePreference = findPreference("full_screen_browsing_mode");
151         hideAppBarPreference = findPreference("hide_app_bar");
152         clearEverythingPreference = findPreference("clear_everything");
153         clearCookiesPreference = findPreference("clear_cookies");
154         clearDomStoragePreference = findPreference("clear_dom_storage");
155         clearFormDataPreference = findPreference("clear_form_data");  // The clear form data preference can be removed once the minimum API >= 26.
156         clearLogcatPreference = findPreference(getString(R.string.clear_logcat_key));
157         clearCachePreference = findPreference("clear_cache");
158         homepagePreference = findPreference("homepage");
159         fontSizePreference = findPreference("font_size");
160         openIntentsInNewTabPreference = findPreference("open_intents_in_new_tab");
161         swipeToRefreshPreference = findPreference("swipe_to_refresh");
162         downloadWithExternalAppPreference = findPreference(getString(R.string.download_with_external_app_key));
163         scrollAppBarPreference = findPreference(getString(R.string.scroll_app_bar_key));
164         bottomAppBarPreference = findPreference(getString(R.string.bottom_app_bar_key));
165         displayAdditionalAppBarIconsPreference = findPreference(getString(R.string.display_additional_app_bar_icons_key));
166         appThemePreference = findPreference("app_theme");
167         webViewThemePreference = findPreference("webview_theme");
168         wideViewportPreference = findPreference("wide_viewport");
169         displayWebpageImagesPreference = findPreference("display_webpage_images");
170
171         // Remove the lint warnings below that the preferences might be null.
172         assert javaScriptPreference != null;
173         assert cookiesPreference != null;
174         assert domStoragePreference != null;
175         assert formDataPreference != null;
176         assert userAgentPreference != null;
177         assert customUserAgentPreference != null;
178         assert incognitoModePreference != null;
179         assert allowScreenshotsPreference != null;
180         assert easyListPreference != null;
181         assert easyPrivacyPreference != null;
182         assert fanboyAnnoyanceListPreference != null;
183         assert fanboySocialBlockingListPreference != null;
184         assert ultraListPreference != null;
185         assert ultraPrivacyPreference != null;
186         assert blockAllThirdPartyRequestsPreference != null;
187         assert trackingQueriesPreference != null;
188         assert ampRedirectsPreference != null;
189         assert searchPreference != null;
190         assert searchCustomURLPreference != null;
191         assert proxyPreference != null;
192         assert proxyCustomUrlPreference != null;
193         assert fullScreenBrowsingModePreference != null;
194         assert hideAppBarPreference != null;
195         assert clearEverythingPreference != null;
196         assert clearCookiesPreference != null;
197         assert clearDomStoragePreference != null;
198         assert clearFormDataPreference != null;
199         assert clearLogcatPreference != null;
200         assert clearCachePreference != null;
201         assert homepagePreference != null;
202         assert fontSizePreference != null;
203         assert openIntentsInNewTabPreference != null;
204         assert swipeToRefreshPreference != null;
205         assert downloadWithExternalAppPreference != null;
206         assert scrollAppBarPreference != null;
207         assert bottomAppBarPreference != null;
208         assert displayAdditionalAppBarIconsPreference != null;
209         assert appThemePreference != null;
210         assert webViewThemePreference != null;
211         assert wideViewportPreference != null;
212         assert displayWebpageImagesPreference != null;
213
214         // Set the preference dependencies.
215         hideAppBarPreference.setDependency("full_screen_browsing_mode");
216         domStoragePreference.setDependency("javascript");
217
218         // Get strings from the preferences.
219         String userAgentName = sharedPreferences.getString("user_agent", getString(R.string.user_agent_default_value));
220         String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value));
221         String proxyString = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value));
222
223         // Get booleans that are used in multiple places from the preferences.
224         boolean javaScriptEnabled = sharedPreferences.getBoolean("javascript", false);
225         boolean fanboyAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboys_annoyance_list", true);
226         boolean fanboySocialBlockingEnabled = sharedPreferences.getBoolean("fanboys_social_blocking_list", true);
227         boolean fullScreenBrowsingMode = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
228         boolean clearEverything = sharedPreferences.getBoolean("clear_everything", true);
229
230         // Remove the form data preferences if the API is >= 26 as they no longer do anything.
231         if (Build.VERSION.SDK_INT >= 26) {
232             // Get handles for the categories.
233             PreferenceCategory privacyCategory = findPreference("privacy");
234             PreferenceCategory clearAndExitCategory = findPreference("clear_and_exit");
235
236             // Remove the incorrect lint warnings below that the preference categories might be null.
237             assert privacyCategory != null;
238             assert clearAndExitCategory != null;
239
240             // Remove the form data preferences.
241             privacyCategory.removePreference(formDataPreference);
242             clearAndExitCategory.removePreference(clearFormDataPreference);
243         }
244
245         // Only enable Fanboy's social blocking list preference if Fanboy's annoyance list is disabled.
246         fanboySocialBlockingListPreference.setEnabled(!fanboyAnnoyanceListEnabled);
247
248
249         // Inflate a WebView to get the default user agent.
250         LayoutInflater inflater = getActivity().getLayoutInflater();
251
252         // `@SuppressLint("InflateParams")` removes the warning about using `null` as the `ViewGroup`, which in this case makes sense because the `bare_webview` will not be displayed.
253         @SuppressLint("InflateParams") View bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false);
254
255         // Get a handle for a bare WebView.
256         WebView bareWebView = bareWebViewLayout.findViewById(R.id.bare_webview);
257
258         // Get the default user agent.
259         defaultUserAgent = bareWebView.getSettings().getUserAgentString();
260
261         // Get the user agent arrays.
262         userAgentNamesArray = ArrayAdapter.createFromResource(requireContext(), R.array.user_agent_names, R.layout.spinner_item);
263         translatedUserAgentNamesArray = resources.getStringArray(R.array.translated_user_agent_names);
264         userAgentDataArray = resources.getStringArray(R.array.user_agent_data);
265
266         // Get the array position of the user agent name.
267         int userAgentArrayPosition = userAgentNamesArray.getPosition(userAgentName);
268
269         // Populate the user agent summary.
270         switch (userAgentArrayPosition) {
271             case MainWebViewActivity.UNRECOGNIZED_USER_AGENT:  // The user agent name is not on the canonical list.
272                 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.  Use the current user agent entry name as the summary.
273                 userAgentPreference.setSummary(userAgentName);
274                 break;
275
276             case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
277                 // Get the user agent text from the webview (which changes based on the version of Android and WebView installed).
278                 userAgentPreference.setSummary(translatedUserAgentNamesArray[userAgentArrayPosition] + ":\n" + defaultUserAgent);
279                 break;
280
281             case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
282                 // Set the summary text.
283                 userAgentPreference.setSummary(R.string.custom_user_agent);
284                 break;
285
286             default:
287                 // Get the user agent summary from the user agent data array.
288                 userAgentPreference.setSummary(translatedUserAgentNamesArray[userAgentArrayPosition] + ":\n" + userAgentDataArray[userAgentArrayPosition]);
289         }
290
291         // Set the summary text for the custom user agent preference.
292         customUserAgentPreference.setSummary(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)));
293
294         // Only enable the custom user agent preference if the user agent is set to `Custom`.
295         customUserAgentPreference.setEnabled(Objects.equals(userAgentPreference.getSummary(), getString(R.string.custom_user_agent)));
296
297         // Set the search URL as the summary text for the search preference when the preference screen is loaded.
298         if (searchString.equals("Custom URL")) {
299             // Use R.string.custom_url, which will be translated, instead of the array value, which will not.
300             searchPreference.setSummary(R.string.custom_url);
301         } else {
302             // Set the array value as the summary text.
303             searchPreference.setSummary(searchString);
304         }
305
306         // Set the summary text for the search custom URL (the default is `""`).
307         searchCustomURLPreference.setSummary(sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value)));
308
309         // Only enable the search custom URL preference if the search is set to `Custom URL`.
310         searchCustomURLPreference.setEnabled(searchString.equals("Custom URL"));
311
312
313         // Set the summary text for the proxy preference when the preference screen is loaded.
314         switch (proxyString) {
315             case ProxyHelper.NONE:
316                 proxyPreference.setSummary(getString(R.string.no_proxy_enabled));
317                 break;
318
319             case ProxyHelper.TOR:
320                 proxyPreference.setSummary(getString(R.string.tor_enabled));
321                 break;
322
323             case ProxyHelper.I2P:
324                 proxyPreference.setSummary(getString(R.string.i2p_enabled));
325                 break;
326
327             case ProxyHelper.CUSTOM:
328                 proxyPreference.setSummary(getString(R.string.custom_proxy));
329                 break;
330         }
331
332         // Set the summary text for the custom proxy URL.
333         proxyCustomUrlPreference.setSummary(sharedPreferences.getString(getString(R.string.proxy_custom_url_key), getString(R.string.proxy_custom_url_default_value)));
334
335         // Only enable the custom proxy URL if a custom proxy is selected.
336         proxyCustomUrlPreference.setEnabled(proxyString.equals(ProxyHelper.CUSTOM));
337
338
339         // Set the status of the clear and exit preferences.
340         clearCookiesPreference.setEnabled(!clearEverything);
341         clearDomStoragePreference.setEnabled(!clearEverything);
342         clearFormDataPreference.setEnabled(!clearEverything);  // The form data line can be removed once the minimum API is >= 26.
343         clearLogcatPreference.setEnabled(!clearEverything);
344         clearCachePreference.setEnabled(!clearEverything);
345
346
347         // Set the homepage URL as the summary text for the homepage preference.
348         homepagePreference.setSummary(sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
349
350
351         // Set the font size as the summary text for the preference.
352         fontSizePreference.setSummary(sharedPreferences.getString("font_size", getString(R.string.font_size_default_value)) + "%");
353
354
355         // Get the app theme string arrays.
356         appThemeEntriesStringArray = resources.getStringArray(R.array.app_theme_entries);
357         appThemeEntryValuesStringArray = resources.getStringArray(R.array.app_theme_entry_values);
358
359         // Get the current app theme.
360         String currentAppTheme = sharedPreferences.getString("app_theme", getString(R.string.app_theme_default_value));
361
362         // Define an app theme entry number.
363         int appThemeEntryNumber;
364
365         // Get the app theme entry number that matches the current app theme.  A switch statement cannot be used because the theme entry values string array is not a compile time constant.
366         if (currentAppTheme.equals(appThemeEntryValuesStringArray[1])) {  // The light theme is selected.
367             // Store the app theme entry number.
368             appThemeEntryNumber = 1;
369         } else if (currentAppTheme.equals(appThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
370             // Store the app theme entry number.
371             appThemeEntryNumber = 2;
372         } else {  // The system default theme is selected.
373             // Store the app theme entry number.
374             appThemeEntryNumber = 0;
375         }
376
377         // Set the current theme as the summary text for the preference.
378         appThemePreference.setSummary(appThemeEntriesStringArray[appThemeEntryNumber]);
379
380
381         // Get the WebView theme string arrays.
382         webViewThemeEntriesStringArray = resources.getStringArray(R.array.webview_theme_entries);
383         webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values);
384
385         // Get the current WebView theme.
386         String currentWebViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value));
387
388         // Define a WebView theme entry number.
389         int webViewThemeEntryNumber;
390
391         // Get the WebView theme entry number that matches the current WebView theme.  A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
392         if (currentWebViewTheme.equals(webViewThemeEntryValuesStringArray[1])) {  // The light theme is selected.
393             // Store the WebView theme entry number.
394             webViewThemeEntryNumber = 1;
395         } else if (currentWebViewTheme.equals(webViewThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
396             // Store the WebView theme entry number.
397             webViewThemeEntryNumber = 2;
398         } else {  // The system default theme is selected.
399             // Store the WebView theme entry number.
400             webViewThemeEntryNumber = 0;
401         }
402
403         // Set the current theme as the summary text for the preference.
404         webViewThemePreference.setSummary(webViewThemeEntriesStringArray[webViewThemeEntryNumber]);
405
406
407         // Set the JavaScript icon.
408         if (javaScriptEnabled) {
409             javaScriptPreference.setIcon(R.drawable.javascript_enabled);
410         } else {
411             javaScriptPreference.setIcon(R.drawable.privacy_mode);
412         }
413
414         // Set the cookies icon.
415         if (sharedPreferences.getBoolean(getString(R.string.cookies_key), false)) {
416             cookiesPreference.setIcon(R.drawable.cookies_enabled);
417         } else {
418             cookiesPreference.setIcon(R.drawable.cookies_disabled);
419         }
420
421         // Set the DOM storage icon.
422         if (javaScriptEnabled) {  // The preference is enabled.
423             if (sharedPreferences.getBoolean("dom_storage", false)) {  // DOM storage is enabled.
424                 domStoragePreference.setIcon(R.drawable.dom_storage_enabled);
425             } else {  // DOM storage is disabled.
426                 domStoragePreference.setIcon(R.drawable.dom_storage_disabled);
427             }
428         } else {  // The preference is disabled.  The icon should be ghosted.
429             domStoragePreference.setIcon(R.drawable.dom_storage_ghosted);
430         }
431
432         // Set the save form data icon if API < 26.  Save form data has no effect on API >= 26.
433         if (Build.VERSION.SDK_INT < 26) {
434             if (sharedPreferences.getBoolean("save_form_data", false)) {
435                 formDataPreference.setIcon(R.drawable.form_data_enabled);
436             } else {
437                 formDataPreference.setIcon(R.drawable.form_data_disabled);
438             }
439         }
440
441         // Set the custom user agent icon.
442         if (customUserAgentPreference.isEnabled()) {
443             customUserAgentPreference.setIcon(R.drawable.custom_user_agent_enabled);
444         } else {
445             customUserAgentPreference.setIcon(R.drawable.custom_user_agent_ghosted);
446         }
447
448         // Set the incognito mode icon.
449         if (sharedPreferences.getBoolean("incognito_mode", false)) {
450             incognitoModePreference.setIcon(R.drawable.incognito_mode_enabled);
451         } else {
452             incognitoModePreference.setIcon(R.drawable.incognito_mode_disabled);
453         }
454
455         // Set the allow screenshots icon.
456         if (sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)) {
457             allowScreenshotsPreference.setIcon(R.drawable.allow_screenshots_enabled);
458         } else {
459             allowScreenshotsPreference.setIcon(R.drawable.allow_screenshots_disabled);
460         }
461
462         // Set the EasyList icon.
463         if (sharedPreferences.getBoolean("easylist", true)) {
464             easyListPreference.setIcon(R.drawable.block_ads_enabled);
465         } else {
466             easyListPreference.setIcon(R.drawable.block_ads_disabled);
467         }
468
469         // Set the EasyPrivacy icon.
470         if (sharedPreferences.getBoolean("easyprivacy", true)) {
471             easyPrivacyPreference.setIcon(R.drawable.block_tracking_enabled);
472         } else {
473             easyPrivacyPreference.setIcon(R.drawable.block_tracking_disabled);
474         }
475
476         // Set the Fanboy lists icons.
477         if (fanboyAnnoyanceListEnabled) {
478             // Set the Fanboy annoyance list icon.
479             fanboyAnnoyanceListPreference.setIcon(R.drawable.social_media_enabled);
480
481             // Set the Fanboy social blocking list icon.
482             fanboySocialBlockingListPreference.setIcon(R.drawable.social_media_ghosted);
483         } else {
484             // Set the Fanboy annoyance list icon.
485             fanboyAnnoyanceListPreference.setIcon(R.drawable.social_media_disabled);
486
487             // Set the Fanboy social blocking list icon.
488             if (fanboySocialBlockingEnabled) {
489                 fanboySocialBlockingListPreference.setIcon(R.drawable.social_media_enabled);
490             } else {
491                 fanboySocialBlockingListPreference.setIcon(R.drawable.social_media_disabled);
492             }
493         }
494
495         // Set the UltraList icon.
496         if (sharedPreferences.getBoolean("ultralist", true)){
497             ultraListPreference.setIcon(R.drawable.block_ads_enabled);
498         } else {
499             ultraListPreference.setIcon(R.drawable.block_ads_disabled);
500         }
501
502         // Set the UltraPrivacy icon.
503         if (sharedPreferences.getBoolean("ultraprivacy", true)) {
504             ultraPrivacyPreference.setIcon(R.drawable.block_tracking_enabled);
505         } else {
506             ultraPrivacyPreference.setIcon(R.drawable.block_tracking_disabled);
507         }
508
509         // Set the block all third-party requests icon.
510         if (sharedPreferences.getBoolean("block_all_third_party_requests", false)) {
511             blockAllThirdPartyRequestsPreference.setIcon(R.drawable.block_all_third_party_requests_enabled);
512         } else {
513             blockAllThirdPartyRequestsPreference.setIcon(R.drawable.block_all_third_party_requests_disabled);
514         }
515
516         // Set the Tracking Queries icon.
517         if (sharedPreferences.getBoolean(getString(R.string.tracking_queries_key), true)) {
518             trackingQueriesPreference.setIcon(R.drawable.modify_url_enabled);
519         } else {
520             trackingQueriesPreference.setIcon(R.drawable.modify_url_disabled);
521         }
522
523         // Set the AMP Redirects icon.
524         if (sharedPreferences.getBoolean(getString(R.string.amp_redirects_key), true)) {
525             ampRedirectsPreference.setIcon(R.drawable.modify_url_enabled);
526         } else {
527             ampRedirectsPreference.setIcon(R.drawable.modify_url_disabled);
528         }
529
530         // Set the search custom URL icon.
531         if (searchCustomURLPreference.isEnabled()) {
532             searchCustomURLPreference.setIcon(R.drawable.search_custom_enabled);
533         } else {
534             searchCustomURLPreference.setIcon(R.drawable.search_custom_ghosted);
535         }
536
537         // Set the Proxy icons according to the theme and status.
538         if (proxyString.equals(ProxyHelper.NONE)) {  // Proxying is disabled.
539             // Set the main proxy icon to be disabled.
540             proxyPreference.setIcon(R.drawable.proxy_disabled);
541
542             // Set the custom proxy URL icon to be ghosted.
543             proxyCustomUrlPreference.setIcon(R.drawable.proxy_ghosted);
544         } else {  // Proxying is enabled.
545             // Set the main proxy icon to be enabled.
546             proxyPreference.setIcon(R.drawable.proxy_enabled);
547
548             // Set the custom proxy URL icon according to its status.
549             if (proxyCustomUrlPreference.isEnabled()) {
550                 proxyCustomUrlPreference.setIcon(R.drawable.proxy_enabled);
551             } else {
552                 proxyCustomUrlPreference.setIcon(R.drawable.proxy_ghosted);
553             }
554         }
555
556         // Set the full screen browsing mode icons.
557         if (fullScreenBrowsingMode) {  // Full screen browsing mode is enabled.
558             // Set the full screen browsing mode preference icon.
559             fullScreenBrowsingModePreference.setIcon(R.drawable.full_screen_enabled);
560
561             // Set the hide app bar icon.
562             if (sharedPreferences.getBoolean("hide_app_bar", true)) {
563                 hideAppBarPreference.setIcon(R.drawable.app_bar_enabled);
564             } else {
565                 hideAppBarPreference.setIcon(R.drawable.app_bar_disabled);
566             }
567         } else {  // Full screen browsing mode is disabled.
568             // Set the icons.
569             fullScreenBrowsingModePreference.setIcon(R.drawable.full_screen_disabled);
570             hideAppBarPreference.setIcon(R.drawable.app_bar_ghosted);
571         }
572
573         // Set the clear everything preference icon.
574         if (clearEverything) {
575             clearEverythingPreference.setIcon(R.drawable.clear_everything_enabled);
576         } else {
577             clearEverythingPreference.setIcon(R.drawable.clear_everything_disabled);
578         }
579
580         // Set the clear cookies preference icon.
581         if (clearEverything || sharedPreferences.getBoolean("clear_cookies", true)) {
582             clearCookiesPreference.setIcon(R.drawable.clear_cookies_enabled);
583         } else {
584             clearCookiesPreference.setIcon(R.drawable.clear_cookies_disabled);
585         }
586
587         // Set the clear DOM storage preference icon.
588         if (clearEverything || sharedPreferences.getBoolean("clear_dom_storage", true)) {
589             clearDomStoragePreference.setIcon(R.drawable.clear_dom_storage_enabled);
590         } else {
591             clearDomStoragePreference.setIcon(R.drawable.clear_dom_storage_disabled);
592         }
593
594         // Set the clear form data preference icon if the API < 26.  It has no effect on newer versions of Android.
595         if (Build.VERSION.SDK_INT < 26) {
596             if (clearEverything || sharedPreferences.getBoolean("clear_form_data", true)) {
597                 clearFormDataPreference.setIcon(R.drawable.clear_form_data_enabled);
598             } else {
599                 clearFormDataPreference.setIcon(R.drawable.clear_form_data_disabled);
600             }
601         }
602
603         // Set the clear logcat preference icon.
604         if (clearEverything || sharedPreferences.getBoolean(getString(R.string.clear_logcat_key), true)) {
605             clearLogcatPreference.setIcon(R.drawable.clear_logcat_enabled);
606         } else {
607             clearLogcatPreference.setIcon(R.drawable.clear_logcat_disabled);
608         }
609
610         // Set the clear cache preference icon.
611         if (clearEverything || sharedPreferences.getBoolean("clear_cache", true)) {
612             clearCachePreference.setIcon(R.drawable.clear_cache_enabled);
613         } else {
614             clearCachePreference.setIcon(R.drawable.clear_cache_disabled);
615         }
616
617         // Set the open intents in new tab preference icon.
618         if (sharedPreferences.getBoolean("open_intents_in_new_tab", true)) {
619             openIntentsInNewTabPreference.setIcon(R.drawable.tab_enabled);
620         } else {
621             openIntentsInNewTabPreference.setIcon(R.drawable.tab_disabled);
622         }
623
624         // Set the swipe to refresh preference icon.
625         if (sharedPreferences.getBoolean("swipe_to_refresh", true)) {
626             swipeToRefreshPreference.setIcon(R.drawable.refresh_enabled);
627         } else {
628             swipeToRefreshPreference.setIcon(R.drawable.refresh_disabled);
629         }
630
631         // Set the download with external app preference icon.
632         if (sharedPreferences.getBoolean(getString(R.string.download_with_external_app_key), false)) {
633             downloadWithExternalAppPreference.setIcon(R.drawable.download_with_external_app_enabled);
634         } else {
635             downloadWithExternalAppPreference.setIcon(R.drawable.download_with_external_app_disabled);
636         }
637
638         // Set the scroll app bar preference icon.
639         if (sharedPreferences.getBoolean(getString(R.string.scroll_app_bar_key), true)) {
640             scrollAppBarPreference.setIcon(R.drawable.app_bar_enabled);
641         } else {
642             scrollAppBarPreference.setIcon(R.drawable.app_bar_disabled);
643         }
644
645         // Set the bottom app bar preference icon.
646         if (sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)) {
647             bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_enabled);
648         } else {
649             bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_disabled);
650         }
651
652         // Set the display additional app bar icons preference icon.
653         if (sharedPreferences.getBoolean(getString(R.string.display_additional_app_bar_icons_key), false)) {
654             displayAdditionalAppBarIconsPreference.setIcon(R.drawable.more_enabled);
655         } else {
656             displayAdditionalAppBarIconsPreference.setIcon(R.drawable.more_disabled);
657         }
658
659         // Set the WebView theme preference icon.
660         switch (webViewThemeEntryNumber) {
661             case 0:  // The system default WebView theme is selected.
662                 // Set the icon according to the app theme.
663                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
664                     webViewThemePreference.setIcon(R.drawable.webview_light_theme);
665                 } else {
666                     webViewThemePreference.setIcon(R.drawable.webview_dark_theme);
667                 }
668                 break;
669
670             case 1:  // The light WebView theme is selected.
671                 // Set the icon.
672                 webViewThemePreference.setIcon(R.drawable.webview_light_theme);
673                 break;
674
675             case 2:  // The dark WebView theme is selected.
676                 // Set the icon.
677                 webViewThemePreference.setIcon(R.drawable.webview_dark_theme);
678                 break;
679         }
680
681         // Set the wide viewport preference icon.
682         if (sharedPreferences.getBoolean("wide_viewport", true)) {
683             wideViewportPreference.setIcon(R.drawable.wide_viewport_enabled);
684         } else {
685             wideViewportPreference.setIcon(R.drawable.wide_viewport_disabled);
686         }
687
688         // Set the display webpage images preference icon.
689         if (sharedPreferences.getBoolean("display_webpage_images", true)) {
690             displayWebpageImagesPreference.setIcon(R.drawable.images_enabled);
691         } else {
692             displayWebpageImagesPreference.setIcon(R.drawable.images_disabled);
693         }
694     }
695
696     // The listener should be unregistered when the app is paused.
697     @Override
698     public void onPause() {
699         // Run the default commands.
700         super.onPause();
701
702         // Get a handle for the shared preferences.
703         SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();
704
705         // Remove the incorrect lint warning below that the shared preferences might be null.
706         assert sharedPreferences != null;
707
708         // Unregister the shared preference listener.
709         sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
710     }
711
712     // The listener should be re-registered when the app is resumed.
713     @Override
714     public void onResume() {
715         // Run the default commands.
716         super.onResume();
717
718         // Get a new shared preference change listener.
719         sharedPreferenceChangeListener = getSharedPreferenceChangeListener(requireContext());
720
721         // Get a handle for the shared preferences.
722         SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();
723
724         // Remove the incorrect lint warning below that the shared preferences might be null.
725         assert sharedPreferences != null;
726
727         // Re-register the shared preference listener.
728         sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
729     }
730
731     // The context must be passed to the shared preference change listener or else any calls to the system `getString()` will crash if the app has been restarted.
732     // This can be removed at some future point, perhaps after the switch to PreferenceScreenCompat.  It isn't an issue in Privacy Cell.
733     private SharedPreferences.OnSharedPreferenceChangeListener getSharedPreferenceChangeListener(Context context) {
734         // Return the shared preference change listener.
735         return (SharedPreferences sharedPreferences, String key) -> {
736             switch (key) {
737                 case "javascript":
738                     // Update the icons and the DOM storage preference status.
739                     if (sharedPreferences.getBoolean("javascript", false)) {  // The JavaScript preference is enabled.
740                         // Update the icon for the JavaScript preference.
741                         javaScriptPreference.setIcon(R.drawable.javascript_enabled);
742
743                         // Update the status of the DOM storage preference.
744                         domStoragePreference.setEnabled(true);
745
746                         // Update the icon for the DOM storage preference.
747                         if (sharedPreferences.getBoolean("dom_storage", false)) {
748                             domStoragePreference.setIcon(R.drawable.dom_storage_enabled);
749                         } else {
750                             domStoragePreference.setIcon(R.drawable.dom_storage_disabled);
751                         }
752                     } else {  // The JavaScript preference is disabled.
753                         // Update the icon for the JavaScript preference.
754                         javaScriptPreference.setIcon(R.drawable.privacy_mode);
755
756                         // Update the status of the DOM storage preference.
757                         domStoragePreference.setEnabled(false);
758
759                         // Set the icon for DOM storage preference to be ghosted.
760                         domStoragePreference.setIcon(R.drawable.dom_storage_ghosted);
761                     }
762                     break;
763
764                 case "cookies":
765                     // Update the icon.
766                     if (sharedPreferences.getBoolean(context.getString(R.string.cookies_key), false)) {
767                         cookiesPreference.setIcon(R.drawable.cookies_enabled);
768                     } else {
769                         cookiesPreference.setIcon(R.drawable.cookies_disabled);
770                     }
771                     break;
772
773                 case "dom_storage":
774                     // Update the icon.
775                     if (sharedPreferences.getBoolean("dom_storage", false)) {
776                         domStoragePreference.setIcon(R.drawable.dom_storage_enabled);
777                     } else {
778                         domStoragePreference.setIcon(R.drawable.dom_storage_disabled);
779                     }
780                     break;
781
782                 // Save form data can be removed once the minimum API >= 26.
783                 case "save_form_data":
784                     // Update the icon.
785                     if (sharedPreferences.getBoolean("save_form_data", false)) {
786                         formDataPreference.setIcon(R.drawable.form_data_enabled);
787                     } else {
788                         formDataPreference.setIcon(R.drawable.form_data_disabled);
789                     }
790                     break;
791
792                 case "user_agent":
793                     // Get the new user agent name.
794                     String newUserAgentName = sharedPreferences.getString("user_agent", context.getString(R.string.user_agent_default_value));
795
796                     // Get the array position for the new user agent name.
797                     int newUserAgentArrayPosition = userAgentNamesArray.getPosition(newUserAgentName);
798
799                     // Get the translated new user agent name.
800                     String translatedNewUserAgentName = translatedUserAgentNamesArray[newUserAgentArrayPosition];
801
802                     // Populate the user agent summary.
803                     switch (newUserAgentArrayPosition) {
804                         case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
805                             // Get the user agent text from the webview (which changes based on the version of Android and WebView installed).
806                             userAgentPreference.setSummary(translatedNewUserAgentName + ":\n" + defaultUserAgent);
807
808                             // Disable the custom user agent preference.
809                             customUserAgentPreference.setEnabled(false);
810
811                             // Set the custom user agent preference icon.
812                             customUserAgentPreference.setIcon(R.drawable.custom_user_agent_ghosted);
813                             break;
814
815                         case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
816                             // Set the summary text.
817                             userAgentPreference.setSummary(R.string.custom_user_agent);
818
819                             // Enable the custom user agent preference.
820                             customUserAgentPreference.setEnabled(true);
821
822                             // Set the custom user agent preference icon.
823                             customUserAgentPreference.setIcon(R.drawable.custom_user_agent_enabled);
824                             break;
825
826                         default:
827                             // Get the user agent summary from the user agent data array.
828                             userAgentPreference.setSummary(translatedNewUserAgentName + ":\n" + userAgentDataArray[newUserAgentArrayPosition]);
829
830                             // Disable the custom user agent preference.
831                             customUserAgentPreference.setEnabled(false);
832
833                             // Set the custom user agent preference icon.
834                             customUserAgentPreference.setIcon(R.drawable.custom_user_agent_ghosted);
835                     }
836                     break;
837
838                 case "custom_user_agent":
839                     // Set the new custom user agent as the summary text for the preference.
840                     customUserAgentPreference.setSummary(sharedPreferences.getString("custom_user_agent", context.getString(R.string.custom_user_agent_default_value)));
841                     break;
842
843                 case "incognito_mode":
844                     // Update the icon.
845                     if (sharedPreferences.getBoolean("incognito_mode", false)) {
846                         incognitoModePreference.setIcon(R.drawable.incognito_mode_enabled);
847                     } else {
848                         incognitoModePreference.setIcon(R.drawable.incognito_mode_disabled);
849                     }
850                     break;
851
852                 case "allow_screenshots":
853                     // Update the icon.
854                     if (sharedPreferences.getBoolean(context.getString(R.string.allow_screenshots_key), false)) {
855                         allowScreenshotsPreference.setIcon(R.drawable.allow_screenshots_enabled);
856                     } else {
857                         allowScreenshotsPreference.setIcon(R.drawable.allow_screenshots_disabled);
858                     }
859
860                     // Restart Privacy Browser.
861                     restartPrivacyBrowser();
862                     break;
863
864                 case "easylist":
865                     // Update the icon.
866                     if (sharedPreferences.getBoolean("easylist", true)) {
867                         easyListPreference.setIcon(R.drawable.block_ads_enabled);
868                     } else {
869                         easyListPreference.setIcon(R.drawable.block_ads_disabled);
870                     }
871                     break;
872
873                 case "easyprivacy":
874                     // Update the icon.
875                     if (sharedPreferences.getBoolean("easyprivacy", true)) {
876                         easyPrivacyPreference.setIcon(R.drawable.block_tracking_enabled);
877                     } else {
878                         easyPrivacyPreference.setIcon(R.drawable.block_tracking_disabled);
879                     }
880                     break;
881
882                 case "fanboys_annoyance_list":
883                     boolean currentFanboyAnnoyanceList = sharedPreferences.getBoolean("fanboys_annoyance_list", true);
884                     boolean currentFanboySocialBlockingList = sharedPreferences.getBoolean("fanboys_social_blocking_list", true);
885
886                     // Update the Fanboy icons.
887                     if (currentFanboyAnnoyanceList) {  // Fanboy's annoyance list is enabled.
888                         // Update the Fanboy's annoyance list icon.
889                         fanboyAnnoyanceListPreference.setIcon(R.drawable.social_media_enabled);
890
891                         // Update the Fanboy's social blocking list icon.
892                         fanboySocialBlockingListPreference.setIcon(R.drawable.social_media_ghosted);
893                     } else {  // Fanboy's annoyance list is disabled.
894                         // Update the Fanboy's annoyance list icon.
895                         fanboyAnnoyanceListPreference.setIcon(R.drawable.social_media_disabled);
896
897                         // Update the Fanboy's social blocking list icon.
898                         if (currentFanboySocialBlockingList) {
899                             fanboySocialBlockingListPreference.setIcon(R.drawable.social_media_enabled);
900                         } else {
901                             fanboySocialBlockingListPreference.setIcon(R.drawable.social_media_disabled);
902                         }
903                     }
904
905                     // Only enable Fanboy's social blocking list preference if Fanboy's annoyance list preference is disabled.
906                     fanboySocialBlockingListPreference.setEnabled(!currentFanboyAnnoyanceList);
907                     break;
908
909                 case "fanboys_social_blocking_list":
910                     // Update the icon.
911                     if (sharedPreferences.getBoolean("fanboys_social_blocking_list", true)) {
912                         fanboySocialBlockingListPreference.setIcon(R.drawable.social_media_enabled);
913                     } else {
914                         fanboySocialBlockingListPreference.setIcon(R.drawable.social_media_disabled);
915                     }
916                     break;
917
918                 case "ultralist":
919                     // Update the icon.
920                     if (sharedPreferences.getBoolean("ultralist", true)) {
921                         ultraListPreference.setIcon(R.drawable.block_ads_enabled);
922                     } else {
923                         ultraListPreference.setIcon(R.drawable.block_ads_disabled);
924                     }
925                     break;
926
927                 case "ultraprivacy":
928                     // Update the icon.
929                     if (sharedPreferences.getBoolean("ultraprivacy", true)) {
930                         ultraPrivacyPreference.setIcon(R.drawable.block_tracking_enabled);
931                     } else {
932                         ultraPrivacyPreference.setIcon(R.drawable.block_tracking_disabled);
933                     }
934                     break;
935
936                 case "block_all_third_party_requests":
937                     // Update the icon.
938                     if (sharedPreferences.getBoolean("block_all_third_party_requests", false)) {
939                         blockAllThirdPartyRequestsPreference.setIcon(R.drawable.block_all_third_party_requests_enabled);
940                     } else {
941                         blockAllThirdPartyRequestsPreference.setIcon(R.drawable.block_all_third_party_requests_disabled);
942                     }
943                     break;
944
945                 case "tracking_queries":
946                     // Update the icon.
947                     if (sharedPreferences.getBoolean(context.getString(R.string.tracking_queries_key), true)) {
948                         trackingQueriesPreference.setIcon(R.drawable.modify_url_enabled);
949                     } else {
950                         trackingQueriesPreference.setIcon(R.drawable.modify_url_disabled);
951                     }
952                     break;
953
954                 case "amp_redirects":
955                     // Update the icon.
956                     if (sharedPreferences.getBoolean(context.getString(R.string.amp_redirects_key), true)) {
957                         ampRedirectsPreference.setIcon(R.drawable.modify_url_enabled);
958                     } else {
959                         ampRedirectsPreference.setIcon(R.drawable.modify_url_disabled);
960                     }
961                     break;
962
963                 case "search":
964                     // Store the new search string.
965                     String newSearchString = sharedPreferences.getString("search", context.getString(R.string.search_default_value));
966
967                     // Update the search and search custom URL preferences.
968                     if (newSearchString.equals("Custom URL")) {  // `Custom URL` is selected.
969                         // Set the summary text to `R.string.custom_url`, which is translated.
970                         searchPreference.setSummary(R.string.custom_url);
971
972                         // Enable the search custom URL preference.
973                         searchCustomURLPreference.setEnabled(true);
974
975                         // Set the search custom URL preference icon.
976                         searchCustomURLPreference.setIcon(R.drawable.search_custom_enabled);
977                     } else {  // `Custom URL` is not selected.
978                         // Set the summary text to `newSearchString`.
979                         searchPreference.setSummary(newSearchString);
980
981                         // Disable `searchCustomURLPreference`.
982                         searchCustomURLPreference.setEnabled(false);
983
984                         // Set the search custom URL preference icon.
985                         searchCustomURLPreference.setIcon(R.drawable.search_custom_ghosted);
986                     }
987                     break;
988
989                 case "search_custom_url":
990                     // Set the new search custom URL as the summary text for the preference.
991                     searchCustomURLPreference.setSummary(sharedPreferences.getString("search_custom_url", context.getString(R.string.search_custom_url_default_value)));
992                     break;
993
994                 case "proxy":
995                     // Get current proxy string.
996                     String currentProxyString = sharedPreferences.getString("proxy", context.getString(R.string.proxy_default_value));
997
998                     // Update the summary text for the proxy preference.
999                     switch (currentProxyString) {
1000                         case ProxyHelper.NONE:
1001                             proxyPreference.setSummary(context.getString(R.string.no_proxy_enabled));
1002                             break;
1003
1004                         case ProxyHelper.TOR:
1005                             proxyPreference.setSummary(context.getString(R.string.tor_enabled));
1006                             break;
1007
1008                         case ProxyHelper.I2P:
1009                             proxyPreference.setSummary(context.getString(R.string.i2p_enabled));
1010                             break;
1011
1012                         case ProxyHelper.CUSTOM:
1013                             proxyPreference.setSummary(context.getString(R.string.custom_proxy));
1014                             break;
1015                     }
1016
1017                     // Update the status of the custom URL preference.
1018                     proxyCustomUrlPreference.setEnabled(currentProxyString.equals(ProxyHelper.CUSTOM));
1019
1020                     // Update the icons.
1021                     if (currentProxyString.equals(ProxyHelper.NONE)) {  // Proxying is disabled.
1022                         // Set the main proxy icon to be disabled
1023                         proxyPreference.setIcon(R.drawable.proxy_disabled);
1024
1025                         // Set the custom proxy URL icon to be ghosted.
1026                         proxyCustomUrlPreference.setIcon(R.drawable.proxy_ghosted);
1027                     } else {  // Proxying is enabled.
1028                         // Set the main proxy icon to be enabled.
1029                         proxyPreference.setIcon(R.drawable.proxy_enabled);
1030
1031                         /// Set the custom proxy URL icon according to its status.
1032                         if (proxyCustomUrlPreference.isEnabled()) {
1033                             proxyCustomUrlPreference.setIcon(R.drawable.proxy_enabled);
1034                         } else {
1035                             proxyCustomUrlPreference.setIcon(R.drawable.proxy_ghosted);
1036                         }
1037                     }
1038                     break;
1039
1040                 case "proxy_custom_url":
1041                     // Set the summary text for the proxy custom URL.
1042                     proxyCustomUrlPreference.setSummary(sharedPreferences.getString(context.getString(R.string.proxy_custom_url_key), context.getString(R.string.proxy_custom_url_default_value)));
1043                     break;
1044
1045                 case "full_screen_browsing_mode":
1046                     if (sharedPreferences.getBoolean("full_screen_browsing_mode", false)) {  // Full screen browsing is enabled.
1047                         // Set the full screen browsing mode preference icon.
1048                         fullScreenBrowsingModePreference.setIcon(R.drawable.full_screen_enabled);
1049
1050                         // Set the hide app bar preference icon.
1051                         if (sharedPreferences.getBoolean("hide_app_bar", true)) {
1052                             hideAppBarPreference.setIcon(R.drawable.app_bar_enabled);
1053                         } else {
1054                             hideAppBarPreference.setIcon(R.drawable.app_bar_disabled);
1055                         }
1056                     } else {  // Full screen browsing is disabled.
1057                         // Update the icons.
1058                         fullScreenBrowsingModePreference.setIcon(R.drawable.full_screen_disabled);
1059                         hideAppBarPreference.setIcon(R.drawable.app_bar_ghosted);
1060                     }
1061                     break;
1062
1063                 case "hide_app_bar":
1064                     // Update the icon.
1065                     if (sharedPreferences.getBoolean("hide_app_bar", true)) {
1066                         hideAppBarPreference.setIcon(R.drawable.app_bar_enabled);
1067                     } else {  // Hide app bar is disabled.
1068                         // Set the icon according to the theme.
1069                         hideAppBarPreference.setIcon(R.drawable.app_bar_disabled);
1070                     }
1071                     break;
1072
1073                 case "clear_everything":
1074                     // Store the new clear everything status
1075                     boolean newClearEverythingBoolean = sharedPreferences.getBoolean("clear_everything", true);
1076
1077                     // Update the status of the clear and exit preferences.
1078                     clearCookiesPreference.setEnabled(!newClearEverythingBoolean);
1079                     clearDomStoragePreference.setEnabled(!newClearEverythingBoolean);
1080                     clearFormDataPreference.setEnabled(!newClearEverythingBoolean);  // This line can be removed once the minimum API >= 26.
1081                     clearLogcatPreference.setEnabled(!newClearEverythingBoolean);
1082                     clearCachePreference.setEnabled(!newClearEverythingBoolean);
1083
1084                     // Update the clear everything preference icon.
1085                     if (newClearEverythingBoolean) {
1086                         clearEverythingPreference.setIcon(R.drawable.clear_everything_enabled);
1087                     } else {
1088                         clearEverythingPreference.setIcon(R.drawable.clear_everything_disabled);
1089                     }
1090
1091                     // Update the clear cookies preference icon.
1092                     if (newClearEverythingBoolean || sharedPreferences.getBoolean("clear_cookies", true)) {
1093                         clearCookiesPreference.setIcon(R.drawable.clear_cookies_enabled);
1094                     } else {
1095                         clearCookiesPreference.setIcon(R.drawable.clear_cookies_disabled);
1096                     }
1097
1098                     // Update the clear dom storage preference icon.
1099                     if (newClearEverythingBoolean || sharedPreferences.getBoolean("clear_dom_storage", true)) {
1100                         clearDomStoragePreference.setIcon(R.drawable.clear_dom_storage_enabled);
1101                     } else {
1102                         clearDomStoragePreference.setIcon(R.drawable.clear_dom_storage_disabled);
1103                     }
1104
1105                     // Update the clear form data preference icon if the API < 26.
1106                     if (Build.VERSION.SDK_INT < 26) {
1107                         if (newClearEverythingBoolean || sharedPreferences.getBoolean("clear_form_data", true)) {
1108                             clearFormDataPreference.setIcon(R.drawable.clear_form_data_enabled);
1109                         } else {
1110                             clearFormDataPreference.setIcon(R.drawable.clear_form_data_disabled);
1111                         }
1112                     }
1113
1114                     // Update the clear logcat preference icon.
1115                     if (newClearEverythingBoolean || sharedPreferences.getBoolean(context.getString(R.string.clear_logcat_key), true)) {
1116                         clearLogcatPreference.setIcon(R.drawable.clear_logcat_enabled);
1117                     } else {
1118                         clearLogcatPreference.setIcon(R.drawable.clear_cache_disabled);
1119                     }
1120
1121                     // Update the clear cache preference icon.
1122                     if (newClearEverythingBoolean || sharedPreferences.getBoolean("clear_cache", true)) {
1123                         clearCachePreference.setIcon(R.drawable.clear_cache_enabled);
1124                     } else {
1125                         clearCachePreference.setIcon(R.drawable.clear_cache_disabled);
1126                     }
1127                     break;
1128
1129                 case "clear_cookies":
1130                     // Update the icon.
1131                     if (sharedPreferences.getBoolean("clear_cookies", true)) {
1132                         clearCookiesPreference.setIcon(R.drawable.clear_cookies_enabled);
1133                     } else {
1134                         clearCookiesPreference.setIcon(R.drawable.clear_cookies_disabled);
1135                     }
1136                     break;
1137
1138                 case "clear_dom_storage":
1139                     // Update the icon.
1140                     if (sharedPreferences.getBoolean("clear_dom_storage", true)) {
1141                         clearDomStoragePreference.setIcon(R.drawable.clear_dom_storage_enabled);
1142                     } else {
1143                         clearDomStoragePreference.setIcon(R.drawable.clear_dom_storage_disabled);
1144                     }
1145                     break;
1146
1147                 // This section can be removed once the minimum API >= 26.
1148                 case "clear_form_data":
1149                     // Update the icon.
1150                     if (sharedPreferences.getBoolean("clear_form_data", true)) {
1151                         clearFormDataPreference.setIcon(R.drawable.clear_form_data_enabled);
1152                     } else {
1153                         clearFormDataPreference.setIcon(R.drawable.clear_form_data_disabled);
1154                     }
1155                     break;
1156
1157                 case "clear_logcat":
1158                     // Update the icon.
1159                     if (sharedPreferences.getBoolean(context.getString(R.string.clear_logcat_key), true)) {
1160                         clearLogcatPreference.setIcon(R.drawable.clear_logcat_enabled);
1161                     } else {
1162                         clearLogcatPreference.setIcon(R.drawable.clear_logcat_disabled);
1163                     }
1164                     break;
1165
1166                 case "clear_cache":
1167                     // Update the icon.
1168                     if (sharedPreferences.getBoolean("clear_cache", true)) {
1169                         clearCachePreference.setIcon(R.drawable.clear_cache_enabled);
1170                     } else {
1171                         clearCachePreference.setIcon(R.drawable.clear_cache_disabled);
1172                     }
1173                     break;
1174
1175                 case "homepage":
1176                     // Set the new homepage URL as the summary text for the Homepage preference.
1177                     homepagePreference.setSummary(sharedPreferences.getString("homepage", context.getString(R.string.homepage_default_value)));
1178                     break;
1179
1180                 case "font_size":
1181                     // Update the font size summary text.
1182                     fontSizePreference.setSummary(sharedPreferences.getString("font_size", context.getString(R.string.font_size_default_value)) + "%");
1183                     break;
1184
1185                 case "open_intents_in_new_tab":
1186                     // Update the icon.
1187                     if (sharedPreferences.getBoolean("open_intents_in_new_tab", true)) {
1188                         openIntentsInNewTabPreference.setIcon(R.drawable.tab_enabled);
1189                     } else {
1190                         openIntentsInNewTabPreference.setIcon(R.drawable.tab_disabled);
1191                     }
1192                     break;
1193
1194                 case "swipe_to_refresh":
1195                     // Update the icon.
1196                     if (sharedPreferences.getBoolean("swipe_to_refresh", true)) {
1197                         swipeToRefreshPreference.setIcon(R.drawable.refresh_enabled);
1198                     } else {
1199                         swipeToRefreshPreference.setIcon(R.drawable.refresh_disabled);
1200                     }
1201                     break;
1202
1203                 case "download_with_external_app":
1204                     // Update the icon.
1205                     if (sharedPreferences.getBoolean(context.getString(R.string.download_with_external_app_key), false)) {
1206                         downloadWithExternalAppPreference.setIcon(R.drawable.download_with_external_app_enabled);
1207                     } else {
1208                         downloadWithExternalAppPreference.setIcon(R.drawable.download_with_external_app_disabled);
1209                     }
1210                     break;
1211
1212                 case "scroll_app_bar":
1213                     // Update the icon.
1214                     if (sharedPreferences.getBoolean(context.getString(R.string.scroll_app_bar_key), true)) scrollAppBarPreference.setIcon(R.drawable.app_bar_enabled);
1215                     else scrollAppBarPreference.setIcon(R.drawable.app_bar_disabled);
1216
1217                     break;
1218
1219                 case "bottom_app_bar":
1220                     // Update the icon.
1221                     if (sharedPreferences.getBoolean(context.getString(R.string.bottom_app_bar_key), false)) bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_enabled);
1222                     else bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_disabled);
1223
1224                     // Restart Privacy Browser.
1225                     restartPrivacyBrowser();
1226                     break;
1227
1228                 case "display_additional_app_bar_icons":
1229                     // Update the icon.
1230                     if (sharedPreferences.getBoolean(context.getString(R.string.display_additional_app_bar_icons_key), false)) {
1231                         displayAdditionalAppBarIconsPreference.setIcon(R.drawable.more_enabled);
1232                     } else {
1233                         displayAdditionalAppBarIconsPreference.setIcon(R.drawable.more_disabled);
1234                     }
1235                     break;
1236
1237                 case "app_theme":
1238                     // Get the new theme.
1239                     String newAppTheme = sharedPreferences.getString("app_theme", context.getString(R.string.app_theme_default_value));
1240
1241                     // Update the system according to the new theme.  A switch statement cannot be used because the theme entry values string array is not a compile-time constant.
1242                     if (newAppTheme.equals(appThemeEntryValuesStringArray[1])) {  // The light theme is selected.
1243                         // Update the theme preference summary text.
1244                         appThemePreference.setSummary(appThemeEntriesStringArray[1]);
1245
1246                         // Apply the new theme.
1247                         AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
1248                     } else if (newAppTheme.equals(appThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
1249                         // Update the theme preference summary text.
1250                         appThemePreference.setSummary(appThemeEntriesStringArray[2]);
1251
1252                         // Apply the new theme.
1253                         AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
1254                     } else {  // The system default theme is selected.
1255                         // Update the theme preference summary text.
1256                         appThemePreference.setSummary(appThemeEntriesStringArray[0]);
1257
1258                         // Apply the new theme.
1259                         if (Build.VERSION.SDK_INT >= 28) {  // The system default theme is supported.
1260                             // Follow the system default theme.
1261                             AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
1262                         } else {// The system default theme is not supported.
1263                             // Follow the battery saver mode.
1264                             AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY);
1265                         }
1266                     }
1267
1268                     // Update the current theme status.
1269                     currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
1270                     break;
1271
1272                 case "webview_theme":
1273                     // Get the new WebView theme.
1274                     String newWebViewTheme = sharedPreferences.getString("webview_theme", context.getString(R.string.webview_theme_default_value));
1275
1276                     // Define a new WebView theme entry number.
1277                     int newWebViewThemeEntryNumber;
1278
1279                     // Get the webView theme entry number that matches the new WebView theme.  A switch statement cannot be used because the theme entry values string array is not a compile time constant.
1280                     if (newWebViewTheme.equals(webViewThemeEntriesStringArray[1])) {  // The light theme is selected.
1281                         // Store the new WebView theme entry number.
1282                         newWebViewThemeEntryNumber = 1;
1283                     } else if (newWebViewTheme.equals(webViewThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
1284                         // Store the WebView theme entry number.
1285                         newWebViewThemeEntryNumber = 2;
1286                     } else {  // The system default theme is selected.
1287                         // Store the WebView theme entry number.
1288                         newWebViewThemeEntryNumber = 0;
1289                     }
1290
1291                     // Update the icon.
1292                     switch (newWebViewThemeEntryNumber) {
1293                         case 0:  // The system default WebView theme is selected.
1294                             // Set the icon.
1295                             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
1296                                 webViewThemePreference.setIcon(R.drawable.webview_light_theme);
1297                             } else {
1298                                 webViewThemePreference.setIcon(R.drawable.webview_dark_theme);
1299                             }
1300                             break;
1301
1302                         case 1:  // The light theme is selected.
1303                             // Set the icon.
1304                             webViewThemePreference.setIcon(R.drawable.webview_light_theme);
1305                             break;
1306
1307                         case 2:  // The dark theme is selected.
1308                             // Set the icon.
1309                             webViewThemePreference.setIcon(R.drawable.webview_dark_theme);
1310                             break;
1311                     }
1312
1313                     // Set the current theme as the summary text for the preference.
1314                     webViewThemePreference.setSummary(webViewThemeEntriesStringArray[newWebViewThemeEntryNumber]);
1315                     break;
1316
1317                 case "wide_viewport":
1318                     // Update the icon.
1319                     if (sharedPreferences.getBoolean("wide_viewport", true)) {
1320                         wideViewportPreference.setIcon(R.drawable.wide_viewport_enabled);
1321                     } else {
1322                         wideViewportPreference.setIcon(R.drawable.wide_viewport_disabled);
1323                     }
1324                     break;
1325
1326                 case "display_webpage_images":
1327                     // Update the icon.
1328                     if (sharedPreferences.getBoolean("display_webpage_images", true)) {
1329                         displayWebpageImagesPreference.setIcon(R.drawable.images_enabled);
1330                     } else {
1331                         displayWebpageImagesPreference.setIcon(R.drawable.images_disabled);
1332                     }
1333                     break;
1334             }
1335         };
1336     }
1337
1338     private void restartPrivacyBrowser() {
1339         // Create an intent to restart Privacy Browser.
1340         Intent restartIntent = requireActivity().getParentActivityIntent();
1341
1342         // Assert that the intent is not null to remove the lint error below.
1343         assert restartIntent != null;
1344
1345         // `Intent.FLAG_ACTIVITY_CLEAR_TASK` removes all activities from the stack.  It requires `Intent.FLAG_ACTIVITY_NEW_TASK`.
1346         restartIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
1347
1348         // Create a handler to restart the activity.
1349         Handler restartHandler = new Handler(Looper.getMainLooper());
1350
1351         // Create a runnable to restart the activity.
1352         Runnable restartRunnable = () -> {
1353             // Restart the activity.
1354             startActivity(restartIntent);
1355
1356             // Kill this instance of Privacy Browser.  Otherwise, the app exhibits sporadic behavior after the restart.
1357             System.exit(0);
1358         };
1359
1360         // Restart the activity after 400 milliseconds, so that the app has enough time to save the change to the preference.
1361         restartHandler.postDelayed(restartRunnable, 400);
1362     }
1363 }