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