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