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