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