]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.kt
Explain in the settings that displaying under cutouts also displays under the keyboar...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / fragments / SettingsFragment.kt
1 /*
2  * Copyright 2016-2024 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 displayUnderCutoutsPreference: Preference
86     private lateinit var homepagePreference: Preference
87     private lateinit var incognitoModePreference: Preference
88     private lateinit var javaScriptPreference: Preference
89     private lateinit var openIntentsInNewTabPreference: Preference
90     private lateinit var proxyCustomUrlPreference: Preference
91     private lateinit var proxyPreference: Preference
92     private lateinit var scrollAppBarPreference: Preference
93     private lateinit var searchCustomURLPreference: Preference
94     private lateinit var searchPreference: Preference
95     private lateinit var sharedPreferenceChangeListener: OnSharedPreferenceChangeListener
96     private lateinit var swipeToRefreshPreference: Preference
97     private lateinit var trackingQueriesPreference: Preference
98     private lateinit var translatedUserAgentNamesArray: Array<String>
99     private lateinit var ultraListPreference: Preference
100     private lateinit var ultraPrivacyPreference: Preference
101     private lateinit var userAgentDataArray: Array<String>
102     private lateinit var userAgentPreference: Preference
103     private lateinit var userAgentNamesArray: ArrayAdapter<CharSequence>
104     private lateinit var webViewThemeEntriesStringArray: Array<String>
105     private lateinit var webViewThemeEntryValuesStringArray: Array<String>
106     private lateinit var webViewThemePreference: Preference
107     private lateinit var wideViewportPreference: Preference
108
109     override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
110         // Check if the fragment has been restarted.
111         if (savedInstanceState != null) {
112             // Set the fragment restored flag.
113             fragmentRestarted = true
114
115             // Save the scroll Y.
116             scrollY = savedInstanceState.getInt(SCROLL_Y)
117         }
118
119         // Load the preferences from the XML file.
120         setPreferencesFromResource(R.xml.preferences, rootKey)
121
122         // Get a handle for the shared preferences.
123         val sharedPreferences = preferenceScreen.sharedPreferences!!
124
125         // Get handles for the preferences.
126         javaScriptPreference = findPreference(getString(R.string.javascript_key))!!
127         cookiesPreference = findPreference(getString(R.string.cookies_key))!!
128         domStoragePreference = findPreference(getString(R.string.dom_storage_key))!!
129         formDataPreference = findPreference(getString(R.string.save_form_data_key))!!  // The form data preference can be removed once the minimum API >= 26.
130         userAgentPreference = findPreference(getString(R.string.user_agent_key))!!
131         customUserAgentPreference = findPreference(getString(R.string.custom_user_agent_key))!!
132         incognitoModePreference = findPreference(getString(R.string.incognito_mode_key))!!
133         allowScreenshotsPreference = findPreference(getString(R.string.allow_screenshots_key))!!
134         easyListPreference = findPreference(getString(R.string.easylist_key))!!
135         easyPrivacyPreference = findPreference(getString(R.string.easyprivacy_key))!!
136         fanboyAnnoyanceListPreference = findPreference(getString(R.string.fanboys_annoyance_list_key))!!
137         fanboySocialBlockingListPreference = findPreference(getString(R.string.fanboys_social_blocking_list_key))!!
138         ultraListPreference = findPreference(getString(R.string.ultralist_key))!!
139         ultraPrivacyPreference = findPreference(getString(R.string.ultraprivacy_key))!!
140         blockAllThirdPartyRequestsPreference = findPreference(getString(R.string.block_all_third_party_requests_key))!!
141         trackingQueriesPreference = findPreference(getString(R.string.tracking_queries_key))!!
142         ampRedirectsPreference = findPreference(getString(R.string.amp_redirects_key))!!
143         searchPreference = findPreference(getString(R.string.search_key))!!
144         searchCustomURLPreference = findPreference(getString(R.string.search_custom_url_key))!!
145         proxyPreference = findPreference(getString(R.string.proxy_key))!!
146         proxyCustomUrlPreference = findPreference(getString(R.string.proxy_custom_url_key))!!
147         fullScreenBrowsingModePreference = findPreference(getString(R.string.full_screen_browsing_mode_key))!!
148         hideAppBarPreference = findPreference(getString(R.string.hide_app_bar_key))!!
149         displayUnderCutoutsPreference = findPreference(getString(R.string.display_under_cutouts_key))!!
150         clearEverythingPreference = findPreference(getString(R.string.clear_everything_key))!!
151         clearCookiesPreference = findPreference(getString(R.string.clear_cookies_key))!!
152         clearDomStoragePreference = findPreference(getString(R.string.clear_dom_storage_key))!!
153         clearFormDataPreference = findPreference(getString(R.string.clear_form_data_key))!!  // The clear form data preference can be removed once the minimum API >= 26.
154         clearLogcatPreference = findPreference(getString(R.string.clear_logcat_key))!!
155         clearCachePreference = findPreference(getString(R.string.clear_cache_key))!!
156         homepagePreference = findPreference(getString(R.string.homepage_key))!!
157         fontSizePreference = findPreference(getString(R.string.font_size_key))!!
158         openIntentsInNewTabPreference = findPreference(getString(R.string.open_intents_in_new_tab_key))!!
159         swipeToRefreshPreference = findPreference(getString(R.string.swipe_to_refresh_key))!!
160         downloadWithExternalAppPreference = findPreference(getString(R.string.download_with_external_app_key))!!
161         scrollAppBarPreference = findPreference(getString(R.string.scroll_app_bar_key))!!
162         bottomAppBarPreference = findPreference(getString(R.string.bottom_app_bar_key))!!
163         displayAdditionalAppBarIconsPreference = findPreference(getString(R.string.display_additional_app_bar_icons_key))!!
164         appThemePreference = findPreference(getString(R.string.app_theme_key))!!
165         webViewThemePreference = findPreference(getString(R.string.webview_theme_key))!!
166         wideViewportPreference = findPreference(getString(R.string.wide_viewport_key))!!
167         displayWebpageImagesPreference = findPreference(getString(R.string.display_webpage_images_key))!!
168
169         // Set the preference dependencies.
170         domStoragePreference.dependency = getString(R.string.javascript_key)
171         hideAppBarPreference.dependency = getString(R.string.full_screen_browsing_mode_key)
172         displayUnderCutoutsPreference.dependency = getString(R.string.full_screen_browsing_mode_key)
173
174         // Get strings from the preferences.
175         val userAgentName = sharedPreferences.getString(getString(R.string.user_agent_key), getString(R.string.user_agent_default_value))
176         val searchString = sharedPreferences.getString(getString(R.string.search_key), getString(R.string.search_default_value))
177         val proxyString = sharedPreferences.getString(getString(R.string.proxy_key), getString(R.string.proxy_default_value))
178
179         // Get booleans that are used in multiple places from the preferences.
180         val javaScriptEnabled = sharedPreferences.getBoolean(getString(R.string.javascript_key), false)
181         val fanboyAnnoyanceListEnabled = sharedPreferences.getBoolean(getString(R.string.fanboys_annoyance_list_key), true)
182         val fanboySocialBlockingEnabled = sharedPreferences.getBoolean(getString(R.string.fanboys_social_blocking_list_key), true)
183         val fullScreenBrowsingMode = sharedPreferences.getBoolean(getString(R.string.full_screen_browsing_mode_key), false)
184         val clearEverything = sharedPreferences.getBoolean(getString(R.string.clear_everything_key), true)
185
186         // Remove the form data preferences if the API is >= 26 as they no longer do anything.
187         if (Build.VERSION.SDK_INT >= 26) {
188             // Get handles for the categories.
189             val privacyCategory = findPreference<PreferenceCategory>(getString(R.string.privacy_category_key))!!
190             val clearAndExitCategory = findPreference<PreferenceCategory>(getString(R.string.clear_and_exit_category_key))!!
191
192             // Remove the form data preferences.
193             privacyCategory.removePreference(formDataPreference)
194             clearAndExitCategory.removePreference(clearFormDataPreference)
195         }
196
197         // Only enable Fanboy's social blocking list preference if Fanboy's annoyance list is disabled.
198         fanboySocialBlockingListPreference.isEnabled = !fanboyAnnoyanceListEnabled
199
200
201         // Inflate a WebView to get the default user agent.
202         val inflater = requireActivity().layoutInflater
203
204         // `@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.
205         @SuppressLint("InflateParams") val bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false)
206
207         // Get a handle for the bare WebView.
208         val bareWebView = bareWebViewLayout.findViewById<WebView>(R.id.bare_webview)
209
210         // Get the default user agent.
211         defaultUserAgent = bareWebView.settings.userAgentString
212
213         // Get the user agent arrays.
214         userAgentNamesArray = ArrayAdapter.createFromResource(requireContext(), R.array.user_agent_names, R.layout.spinner_item)
215         translatedUserAgentNamesArray = resources.getStringArray(R.array.translated_user_agent_names)
216         userAgentDataArray = resources.getStringArray(R.array.user_agent_data)
217
218         // Populate the user agent summary.
219         when (val userAgentArrayPosition = userAgentNamesArray.getPosition(userAgentName)) {
220             // The user agent name is not on the canonical list.
221             // 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.
222             UNRECOGNIZED_USER_AGENT -> userAgentPreference.summary = userAgentName
223
224             // Get the user agent text from the webview (which changes based on the version of Android and WebView installed).
225             SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentPreference.summary = "${translatedUserAgentNamesArray[userAgentArrayPosition]}:\n$defaultUserAgent"
226
227             // Display the custom user agent.
228             SETTINGS_CUSTOM_USER_AGENT -> userAgentPreference.setSummary(R.string.custom_user_agent)
229
230             // Get the user agent summary from the user agent data array.
231             else -> userAgentPreference.summary = "${translatedUserAgentNamesArray[userAgentArrayPosition]}:\n${userAgentDataArray[userAgentArrayPosition]}"
232         }
233
234         // Set the summary text for the custom user agent preference.
235         customUserAgentPreference.summary = sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value))
236
237         // Only enable the custom user agent preference if the user agent is set to custom.
238         customUserAgentPreference.isEnabled = (userAgentPreference.summary == getString(R.string.custom_user_agent))
239
240         // Set the search URL as the summary text for the search preference when the preference screen is loaded.
241         if (searchString == getString(R.string.custom_url_item)) {
242             // Use R.string.custom_url, which will be translated, instead of the array value, which will not.
243             searchPreference.setSummary(R.string.custom_url)
244         } else {
245             // Set the array value as the summary text.
246             searchPreference.summary = searchString
247         }
248
249         // Set the summary text for the search custom URL (the default is `""`).
250         searchCustomURLPreference.summary = sharedPreferences.getString(getString(R.string.search_custom_url_key), getString(R.string.search_custom_url_default_value))
251
252         // Only enable the search custom URL preference if the search is set to a custom URL.
253         searchCustomURLPreference.isEnabled = (searchString == getString(R.string.custom_url_item))
254
255         // Set the summary text for the proxy preference.
256         proxyPreference.summary = when (proxyString) {
257             ProxyHelper.NONE -> getString(R.string.no_proxy_enabled)
258             ProxyHelper.TOR -> getString(R.string.tor_enabled)
259             ProxyHelper.I2P -> getString(R.string.i2p_enabled)
260             ProxyHelper.CUSTOM -> getString(R.string.custom_proxy)
261             else -> getString(R.string.no_proxy_enabled)
262         }
263
264         // Set the summary text for the custom proxy URL.
265         proxyCustomUrlPreference.summary = sharedPreferences.getString(getString(R.string.proxy_custom_url_key), getString(R.string.proxy_custom_url_default_value))
266
267         // Only enable the custom proxy URL if a custom proxy is selected.
268         proxyCustomUrlPreference.isEnabled = proxyString == ProxyHelper.CUSTOM
269
270         // Set the status of the clear and exit preferences.
271         clearCookiesPreference.isEnabled = !clearEverything
272         clearDomStoragePreference.isEnabled = !clearEverything
273         clearFormDataPreference.isEnabled = !clearEverything  // Clear form data can be removed once the minimum API is >= 26.
274         clearLogcatPreference.isEnabled = !clearEverything
275         clearCachePreference.isEnabled = !clearEverything
276
277         // Set the homepage URL as the summary text for the homepage preference.
278         homepagePreference.summary = sharedPreferences.getString(getString(R.string.homepage_key), getString(R.string.homepage_default_value))
279
280         // Set the font size as the summary text for the preference.
281         fontSizePreference.summary = sharedPreferences.getString(getString(R.string.font_size_key), getString(R.string.font_size_default_value)) + "%"
282
283         // Get the app theme string arrays.
284         appThemeEntriesStringArray = resources.getStringArray(R.array.app_theme_entries)
285         appThemeEntryValuesStringArray = resources.getStringArray(R.array.app_theme_entry_values)
286
287         // Get the app theme entry number that matches the current app theme.
288         val appThemeEntryNumber: Int = when (sharedPreferences.getString(getString(R.string.app_theme_key), getString(R.string.app_theme_default_value))) {
289             appThemeEntryValuesStringArray[1] -> 1  // The light theme is selected.
290             appThemeEntryValuesStringArray[2] -> 2  // The dark theme is selected.
291             else -> 0  // The system default theme is selected.
292         }
293
294         // Set the current theme as the summary text for the preference.
295         appThemePreference.summary = appThemeEntriesStringArray[appThemeEntryNumber]
296
297         // Enable the WebView theme preference if the app theme is not set to light.  Google does not allow light themes to display dark WebViews.
298         webViewThemePreference.isEnabled = (appThemeEntryNumber != 1)
299
300         // Get the WebView theme string arrays.
301         webViewThemeEntriesStringArray = resources.getStringArray(R.array.webview_theme_entries)
302         webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values)
303
304         // Get the WebView theme entry number that matches the current WebView theme.
305         val webViewThemeEntryNumber: Int = when (sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value))) {
306             webViewThemeEntryValuesStringArray[1] -> 1  // The light theme is selected.
307             webViewThemeEntryValuesStringArray[2] -> 2  // The dark theme is selected.
308             else -> 0  // The system default theme is selected.
309         }
310
311         // Set the current theme as the summary text for the preference.
312         webViewThemePreference.summary = webViewThemeEntriesStringArray[webViewThemeEntryNumber]
313
314         // Set the JavaScript icon.
315         if (javaScriptEnabled)
316             javaScriptPreference.setIcon(R.drawable.javascript_enabled)
317         else
318             javaScriptPreference.setIcon(R.drawable.privacy_mode)
319
320         // Set the cookies icon.
321         if (sharedPreferences.getBoolean(getString(R.string.cookies_key), false))
322             cookiesPreference.setIcon(R.drawable.cookies_enabled)
323         else
324             cookiesPreference.setIcon(R.drawable.cookies_disabled)
325
326         // Set the DOM storage icon.
327         if (javaScriptEnabled) {  // JavaScript is enabled.
328             if (sharedPreferences.getBoolean(getString(R.string.dom_storage_key), false))  // DOM storage is enabled.
329                 domStoragePreference.setIcon(R.drawable.dom_storage_enabled)
330             else  // DOM storage is disabled.
331                 domStoragePreference.setIcon(R.drawable.dom_storage_disabled)
332         } else {  // JavaScript is disabled.  DOM storage should be ghosted.
333             domStoragePreference.setIcon(R.drawable.dom_storage_ghosted)
334         }
335
336         // Set the save form data icon if API < 26.  Save form data has no effect on API >= 26.
337         if (Build.VERSION.SDK_INT < 26) {
338             if (sharedPreferences.getBoolean(getString(R.string.save_form_data_key), false))
339                 formDataPreference.setIcon(R.drawable.form_data_enabled)
340             else
341                 formDataPreference.setIcon(R.drawable.form_data_disabled)
342         }
343
344         // Set the custom user agent icon.
345         if (customUserAgentPreference.isEnabled)
346             customUserAgentPreference.setIcon(R.drawable.custom_user_agent_enabled)
347         else
348             customUserAgentPreference.setIcon(R.drawable.custom_user_agent_ghosted)
349
350         // Set the incognito mode icon.
351         if (sharedPreferences.getBoolean(getString(R.string.incognito_mode_key), false))
352             incognitoModePreference.setIcon(R.drawable.incognito_mode_enabled)
353         else
354             incognitoModePreference.setIcon(R.drawable.incognito_mode_disabled)
355
356         // Set the allow screenshots icon.
357         if (sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false))
358             allowScreenshotsPreference.setIcon(R.drawable.allow_screenshots_enabled)
359         else
360             allowScreenshotsPreference.setIcon(R.drawable.allow_screenshots_disabled)
361
362         // Set the EasyList icon.
363         if (sharedPreferences.getBoolean(getString(R.string.easylist_key), true))
364             easyListPreference.setIcon(R.drawable.block_ads_enabled)
365         else
366             easyListPreference.setIcon(R.drawable.block_ads_disabled)
367
368         // Set the EasyPrivacy icon.
369         if (sharedPreferences.getBoolean(getString(R.string.easyprivacy_key), true))
370             easyPrivacyPreference.setIcon(R.drawable.block_tracking_enabled)
371         else
372             easyPrivacyPreference.setIcon(R.drawable.block_tracking_disabled)
373
374         // Set the Fanboy lists icons.
375         if (fanboyAnnoyanceListEnabled) {
376             // Set the Fanboy annoyance list icon.
377             fanboyAnnoyanceListPreference.setIcon(R.drawable.social_media_enabled)
378
379             // Set the Fanboy social blocking list icon.
380             fanboySocialBlockingListPreference.setIcon(R.drawable.social_media_ghosted)
381         } else {
382             // Set the Fanboy annoyance list icon.
383             fanboyAnnoyanceListPreference.setIcon(R.drawable.social_media_disabled)
384
385             // Set the Fanboy social blocking list icon.
386             if (fanboySocialBlockingEnabled)
387                 fanboySocialBlockingListPreference.setIcon(R.drawable.social_media_enabled)
388             else
389                 fanboySocialBlockingListPreference.setIcon(R.drawable.social_media_disabled)
390         }
391
392         // Set the UltraList icon.
393         if (sharedPreferences.getBoolean(getString(R.string.ultralist_key), true))
394             ultraListPreference.setIcon(R.drawable.block_ads_enabled)
395         else
396             ultraListPreference.setIcon(R.drawable.block_ads_disabled)
397
398         // Set the UltraPrivacy icon.
399         if (sharedPreferences.getBoolean(getString(R.string.ultraprivacy_key), true))
400             ultraPrivacyPreference.setIcon(R.drawable.block_tracking_enabled)
401         else
402             ultraPrivacyPreference.setIcon(R.drawable.block_tracking_disabled)
403
404         // Set the block all third-party requests icon.
405         if (sharedPreferences.getBoolean(getString(R.string.block_all_third_party_requests), false))
406             blockAllThirdPartyRequestsPreference.setIcon(R.drawable.block_all_third_party_requests_enabled)
407         else
408             blockAllThirdPartyRequestsPreference.setIcon(R.drawable.block_all_third_party_requests_disabled)
409
410         // Set the Tracking Queries icon.
411         if (sharedPreferences.getBoolean(getString(R.string.tracking_queries_key), true))
412             trackingQueriesPreference.setIcon(R.drawable.modify_url_enabled)
413         else
414             trackingQueriesPreference.setIcon(R.drawable.modify_url_disabled)
415
416         // Set the AMP Redirects icon.
417         if (sharedPreferences.getBoolean(getString(R.string.amp_redirects_key), true))
418             ampRedirectsPreference.setIcon(R.drawable.modify_url_enabled)
419         else
420             ampRedirectsPreference.setIcon(R.drawable.modify_url_disabled)
421
422         // Set the search custom URL icon.
423         if (searchCustomURLPreference.isEnabled)
424             searchCustomURLPreference.setIcon(R.drawable.search_custom_enabled)
425         else
426             searchCustomURLPreference.setIcon(R.drawable.search_custom_ghosted)
427
428         // Set the proxy icons according to the theme and status.
429         if (proxyString == ProxyHelper.NONE) {  // Proxying is disabled.
430             // Set the main proxy icon to be disabled.
431             proxyPreference.setIcon(R.drawable.proxy_disabled)
432
433             // Set the custom proxy URL icon to be ghosted.
434             proxyCustomUrlPreference.setIcon(R.drawable.proxy_ghosted)
435         } else {  // Proxying is enabled.
436             // Set the main proxy icon to be enabled.
437             proxyPreference.setIcon(R.drawable.proxy_enabled)
438
439             // Set the custom proxy URL icon according to its status.
440             if (proxyCustomUrlPreference.isEnabled)
441                 proxyCustomUrlPreference.setIcon(R.drawable.proxy_enabled)
442             else
443                 proxyCustomUrlPreference.setIcon(R.drawable.proxy_ghosted)
444         }
445
446         // Set the full-screen browsing mode icons.
447         if (fullScreenBrowsingMode) {  // Full-screen browsing mode is enabled.
448             // Set the full screen browsing mode preference icon.
449             fullScreenBrowsingModePreference.setIcon(R.drawable.full_screen_enabled)
450
451             // Set the hide app bar icon.
452             if (sharedPreferences.getBoolean(getString(R.string.hide_app_bar_key), true))
453                 hideAppBarPreference.setIcon(R.drawable.app_bar_enabled)
454             else
455                 hideAppBarPreference.setIcon(R.drawable.app_bar_disabled)
456         } else {  // Full screen browsing mode is disabled.
457             // Set the icons.
458             fullScreenBrowsingModePreference.setIcon(R.drawable.full_screen_disabled)
459             hideAppBarPreference.setIcon(R.drawable.app_bar_ghosted)
460         }
461
462         // Set the display under cutouts icon.
463         if (sharedPreferences.getBoolean(getString(R.string.display_under_cutouts_key), false))
464             displayUnderCutoutsPreference.setIcon(R.drawable.display_under_cutouts_enabled)
465         else
466             displayUnderCutoutsPreference.setIcon(R.drawable.display_under_cutouts_disabled)
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), false))
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.post { 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.display_under_cutouts_key) -> {
946                     // Update the icon.
947                     if (sharedPreferences.getBoolean(getString(R.string.display_under_cutouts_key), true))
948                         displayUnderCutoutsPreference.setIcon(R.drawable.display_under_cutouts_enabled)
949                     else
950                         displayUnderCutoutsPreference.setIcon(R.drawable.display_under_cutouts_disabled)
951
952                     // Restart Privacy Browser.
953                     restartPrivacyBrowser()
954                 }
955
956                 getString(R.string.clear_everything_key) -> {
957                     // Store the new clear everything status
958                     val newClearEverythingBoolean = sharedPreferences.getBoolean(getString(R.string.clear_everything_key), true)
959
960                     // Update the status of the clear and exit preferences.
961                     clearCookiesPreference.isEnabled = !newClearEverythingBoolean
962                     clearDomStoragePreference.isEnabled = !newClearEverythingBoolean
963                     clearFormDataPreference.isEnabled = !newClearEverythingBoolean  // Clear form data can be removed once the minimum API >= 26.
964                     clearLogcatPreference.isEnabled = !newClearEverythingBoolean
965                     clearCachePreference.isEnabled = !newClearEverythingBoolean
966
967                     // Update the clear everything preference icon.
968                     if (newClearEverythingBoolean)
969                         clearEverythingPreference.setIcon(R.drawable.clear_everything_enabled)
970                     else
971                         clearEverythingPreference.setIcon(R.drawable.clear_everything_disabled)
972
973                     // Update the clear cookies preference icon.
974                     if (newClearEverythingBoolean || sharedPreferences.getBoolean(getString(R.string.clear_cookies_key), true))
975                         clearCookiesPreference.setIcon(R.drawable.clear_cookies_enabled)
976                     else
977                         clearCookiesPreference.setIcon(R.drawable.clear_cookies_disabled)
978
979                     // Update the clear dom storage preference icon.
980                     if (newClearEverythingBoolean || sharedPreferences.getBoolean(getString(R.string.clear_dom_storage_key), true))
981                         clearDomStoragePreference.setIcon(R.drawable.clear_dom_storage_enabled)
982                     else
983                         clearDomStoragePreference.setIcon(R.drawable.clear_dom_storage_disabled)
984
985                     // Update the clear form data preference icon if the API < 26.
986                     if (Build.VERSION.SDK_INT < 26) {
987                         if (newClearEverythingBoolean || sharedPreferences.getBoolean(getString(R.string.clear_form_data_key), true))
988                             clearFormDataPreference.setIcon(R.drawable.clear_form_data_enabled)
989                         else
990                             clearFormDataPreference.setIcon(R.drawable.clear_form_data_disabled)
991                     }
992
993                     // Update the clear logcat preference icon.
994                     if (newClearEverythingBoolean || sharedPreferences.getBoolean(getString(R.string.clear_logcat_key), true))
995                         clearLogcatPreference.setIcon(R.drawable.clear_logcat_enabled)
996                     else
997                         clearLogcatPreference.setIcon(R.drawable.clear_logcat_disabled)
998
999                     // Update the clear cache preference icon.
1000                     if (newClearEverythingBoolean || sharedPreferences.getBoolean(getString(R.string.clear_cache_key), true))
1001                         clearCachePreference.setIcon(R.drawable.clear_cache_enabled)
1002                     else
1003                         clearCachePreference.setIcon(R.drawable.clear_cache_disabled)
1004                 }
1005
1006                 getString(R.string.clear_cookies_key) -> {
1007                     // Update the icon.
1008                     if (sharedPreferences.getBoolean(getString(R.string.clear_cookies_key), true))
1009                         clearCookiesPreference.setIcon(R.drawable.clear_cookies_enabled)
1010                     else
1011                         clearCookiesPreference.setIcon(R.drawable.clear_cookies_disabled)
1012                 }
1013
1014                 getString(R.string.clear_dom_storage_key) -> {
1015                     // Update the icon.
1016                     if (sharedPreferences.getBoolean(getString(R.string.clear_dom_storage_key), true))
1017                         clearDomStoragePreference.setIcon(R.drawable.clear_dom_storage_enabled)
1018                     else
1019                         clearDomStoragePreference.setIcon(R.drawable.clear_dom_storage_disabled)
1020                 }
1021
1022                 getString(R.string.clear_form_data_key) -> {
1023                     // Update the icon.
1024                     if (sharedPreferences.getBoolean(getString(R.string.clear_form_data_key), true))
1025                         clearFormDataPreference.setIcon(R.drawable.clear_form_data_enabled)
1026                     else
1027                         clearFormDataPreference.setIcon(R.drawable.clear_form_data_disabled)
1028                 }
1029
1030                 getString(R.string.clear_logcat_key) -> {
1031                     // Update the icon.
1032                     if (sharedPreferences.getBoolean(getString(R.string.clear_logcat_key), true))
1033                         clearLogcatPreference.setIcon(R.drawable.clear_logcat_enabled)
1034                     else
1035                         clearLogcatPreference.setIcon(R.drawable.clear_logcat_disabled)
1036                 }
1037
1038                 getString(R.string.clear_cache_key) -> {
1039                     // Update the icon.
1040                     if (sharedPreferences.getBoolean(getString(R.string.clear_cache_key), true))
1041                         clearCachePreference.setIcon(R.drawable.clear_cache_enabled)
1042                     else
1043                         clearCachePreference.setIcon(R.drawable.clear_cache_disabled)
1044                 }
1045
1046                 getString(R.string.homepage_key) -> {
1047                     // Set the new homepage URL as the summary text for the Homepage preference.
1048                     homepagePreference.summary = sharedPreferences.getString(getString(R.string.homepage_key), getString(R.string.homepage_default_value))
1049                 }
1050
1051                 getString(R.string.font_size_key) -> {
1052                     // Update the font size summary text.
1053                     fontSizePreference.summary = sharedPreferences.getString(getString(R.string.font_size_key), getString(R.string.font_size_default_value)) + "%"
1054                 }
1055
1056                 getString(R.string.open_intents_in_new_tab_key) -> {
1057                     // Update the icon.
1058                     if (sharedPreferences.getBoolean(getString(R.string.open_intents_in_new_tab_key), true))
1059                         openIntentsInNewTabPreference.setIcon(R.drawable.tab_enabled)
1060                     else
1061                         openIntentsInNewTabPreference.setIcon(R.drawable.tab_disabled)
1062                 }
1063
1064                 getString(R.string.swipe_to_refresh_key) -> {
1065                     // Update the icon.
1066                     if (sharedPreferences.getBoolean(getString(R.string.swipe_to_refresh_key), true))
1067                         swipeToRefreshPreference.setIcon(R.drawable.refresh_enabled)
1068                     else
1069                         swipeToRefreshPreference.setIcon(R.drawable.refresh_disabled)
1070                 }
1071
1072                 getString(R.string.download_with_external_app_key) -> {
1073                     // Update the icon.
1074                     if (sharedPreferences.getBoolean(getString(R.string.download_with_external_app_key), false))
1075                         downloadWithExternalAppPreference.setIcon(R.drawable.download_with_external_app_enabled)
1076                     else
1077                         downloadWithExternalAppPreference.setIcon(R.drawable.download_with_external_app_disabled)
1078                 }
1079
1080                 getString(R.string.scroll_app_bar_key) -> {
1081                     // Update the icon.
1082                     if (sharedPreferences.getBoolean(getString(R.string.scroll_app_bar_key), false))
1083                         scrollAppBarPreference.setIcon(R.drawable.app_bar_enabled)
1084                     else
1085                         scrollAppBarPreference.setIcon(R.drawable.app_bar_disabled)
1086                 }
1087
1088                 getString(R.string.bottom_app_bar_key) -> {
1089                     // Update the icon.
1090                     if (sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false))
1091                         bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_enabled)
1092                     else
1093                         bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_disabled)
1094
1095                     // Restart Privacy Browser.
1096                     restartPrivacyBrowser()
1097                 }
1098
1099                 getString(R.string.display_additional_app_bar_icons_key) -> {
1100                     // Update the icon.
1101                     if (sharedPreferences.getBoolean(getString(R.string.display_additional_app_bar_icons_key), false))
1102                         displayAdditionalAppBarIconsPreference.setIcon(R.drawable.more_enabled)
1103                     else
1104                         displayAdditionalAppBarIconsPreference.setIcon(R.drawable.more_disabled)
1105
1106                     // Restart Privacy Browser.
1107                     restartPrivacyBrowser()
1108                 }
1109
1110                 getString(R.string.app_theme_key) -> {
1111                     // Get the app theme entry number that matches the current app theme.
1112                     val appThemeEntryNumber: Int = when (sharedPreferences.getString(getString(R.string.app_theme_key), getString(R.string.app_theme_default_value))) {
1113                         appThemeEntryValuesStringArray[1] -> 1  // The light theme is selected.
1114                         appThemeEntryValuesStringArray[2] -> 2  // The dark theme is selected.
1115                         else -> 0  // The system default theme is selected.
1116                     }
1117
1118                     // Update the system according to the new theme.
1119                     when (appThemeEntryNumber) {
1120                         0 -> {  // The system default theme is selected.
1121                             // Update the theme preference summary text.
1122                             appThemePreference.summary = appThemeEntriesStringArray[0]
1123
1124                             // Apply the new theme.
1125                             if (Build.VERSION.SDK_INT >= 28) {  // The system default theme is supported.
1126                                 // Follow the system default theme.
1127                                 AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
1128                             } else { // The system default theme is not supported.
1129                                 // Follow the battery saver mode.
1130                                 AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY)
1131                             }
1132                         }
1133
1134                         1 -> {  // The light theme is selected.
1135                             // Update the theme preference summary text.
1136                             appThemePreference.summary = appThemeEntriesStringArray[1]
1137
1138                             // Apply the new theme.
1139                             AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
1140                         }
1141
1142                         2 -> {  // The dark theme is selected.
1143                             // Update the theme preference summary text.
1144                             appThemePreference.summary = appThemeEntriesStringArray[2]
1145
1146                             // Apply the new theme.
1147                             AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
1148                         }
1149                     }
1150
1151                     // Enable the WebView theme preference if the app theme is not set to light.  Google does not allow light themes to display dark WebViews.
1152                     webViewThemePreference.isEnabled = (appThemeEntryNumber != 1)
1153
1154                     // Get the webView theme entry number that matches the new WebView theme.
1155                     val webViewThemeEntryNumber: Int = when (sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value))) {
1156                         webViewThemeEntriesStringArray[1] -> 1  // The light theme is selected.
1157                         webViewThemeEntryValuesStringArray[2] -> 2  // The dark theme is selected.
1158                         else -> 0  // The system default theme is selected.
1159                     }
1160
1161                     // Update the WebView theme icon.
1162                     if (webViewThemePreference.isEnabled) {  // The WebView theme preference is enabled.
1163                         when (webViewThemeEntryNumber) {
1164                             // The system default WebView theme is selected.
1165                             0 -> {
1166                                 // Get the current theme status.
1167                                 val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
1168
1169                                 // Set the icon according to the app theme.
1170                                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
1171                                     webViewThemePreference.setIcon(R.drawable.webview_light_theme)
1172                                 else
1173                                     webViewThemePreference.setIcon(R.drawable.webview_dark_theme)
1174                             }
1175
1176                             // The light WebView theme is selected.
1177                             1 -> {
1178                                 // Set the icon.
1179                                 webViewThemePreference.setIcon(R.drawable.webview_light_theme)
1180                             }
1181
1182                             // The dark WebView theme is selected.
1183                             2 -> {
1184                                 // Set the icon.
1185                                 webViewThemePreference.setIcon(R.drawable.webview_dark_theme)
1186                             }
1187                         }
1188                     } else {  // The WebView theme preference is disabled.
1189                         webViewThemePreference.setIcon(R.drawable.webview_theme_ghosted)
1190                     }
1191                 }
1192
1193                 getString(R.string.webview_theme_key) -> {
1194                     // Get the webView theme entry number that matches the new WebView theme.
1195                     val newWebViewThemeEntryNumber: Int = when (sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value))) {
1196                         webViewThemeEntriesStringArray[1] -> 1  // The light theme is selected.
1197                         webViewThemeEntryValuesStringArray[2] -> 2  // The dark theme is selected.
1198                         else -> 0  // The system default theme is selected.
1199                     }
1200
1201                     // Update the WebView theme icon.
1202                     when (newWebViewThemeEntryNumber) {
1203                         // The system default WebView theme is selected.
1204                         0 -> {
1205                             // Get the current theme status.
1206                             val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
1207
1208                             // Set the icon.
1209                             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
1210                                 webViewThemePreference.setIcon(R.drawable.webview_light_theme)
1211                             else
1212                                 webViewThemePreference.setIcon(R.drawable.webview_dark_theme)
1213                         }
1214
1215                         // The light WebView theme is selected.
1216                         1 -> {
1217                             // Set the icon.
1218                             webViewThemePreference.setIcon(R.drawable.webview_light_theme)
1219                         }
1220
1221                         // The dark WebView theme is selected.
1222                         2 -> {
1223                             // Set the icon.
1224                             webViewThemePreference.setIcon(R.drawable.webview_dark_theme)
1225                         }
1226                     }
1227
1228                     // Set the current theme as the summary text for the preference.
1229                     webViewThemePreference.summary = webViewThemeEntriesStringArray[newWebViewThemeEntryNumber]
1230                 }
1231
1232                 getString(R.string.wide_viewport_key) -> {
1233                     // Update the icon.
1234                     if (sharedPreferences.getBoolean(getString(R.string.wide_viewport_key), true))
1235                         wideViewportPreference.setIcon(R.drawable.wide_viewport_enabled)
1236                     else
1237                         wideViewportPreference.setIcon(R.drawable.wide_viewport_disabled)
1238                 }
1239
1240                 getString(R.string.display_webpage_images_key) -> {
1241                     // Update the icon.
1242                     if (sharedPreferences.getBoolean(getString(R.string.display_webpage_images_key), true))
1243                         displayWebpageImagesPreference.setIcon(R.drawable.images_enabled)
1244                     else
1245                         displayWebpageImagesPreference.setIcon(R.drawable.images_disabled)
1246                 }
1247             }
1248         }
1249     }
1250
1251     private fun restartPrivacyBrowser() {
1252         // Create an intent to restart Privacy Browser.
1253         val restartIntent = requireActivity().parentActivityIntent!!
1254
1255         // `Intent.FLAG_ACTIVITY_CLEAR_TASK` removes all activities from the stack.  It requires `Intent.FLAG_ACTIVITY_NEW_TASK`.
1256         restartIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
1257
1258         // Create a handler to restart the activity.
1259         val restartHandler = Handler(Looper.getMainLooper())
1260
1261         // Create a runnable to restart the activity.
1262         val restartRunnable = Runnable {
1263             // Restart the activity.
1264             startActivity(restartIntent)
1265
1266             // Kill this instance of Privacy Browser.  Otherwise, the app exhibits sporadic behavior after the restart.
1267             exitProcess(0)
1268         }
1269
1270         // Restart the activity after 400 milliseconds, so that the app has enough time to save the change to the preference.
1271         restartHandler.postDelayed(restartRunnable, 400)
1272     }
1273 }