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