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