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