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