]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.kt
Highlight the background of non-default domain settings. https://redmine.stoutner...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / fragments / DomainSettingsFragment.kt
1 /*
2  * Copyright 2017-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.res.Configuration
24 import android.os.Build
25 import android.os.Bundle
26 import android.text.Editable
27 import android.text.SpannableStringBuilder
28 import android.text.Spanned
29 import android.text.TextWatcher
30 import android.text.style.ForegroundColorSpan
31 import android.view.LayoutInflater
32 import android.view.View
33 import android.view.ViewGroup
34 import android.webkit.WebView
35 import android.widget.AdapterView
36 import android.widget.ArrayAdapter
37 import android.widget.CompoundButton
38 import android.widget.EditText
39 import android.widget.ImageView
40 import android.widget.LinearLayout
41 import android.widget.RadioButton
42 import android.widget.ScrollView
43 import android.widget.Spinner
44 import android.widget.TextView
45
46 import androidx.appcompat.widget.SwitchCompat
47 import androidx.cardview.widget.CardView
48 import androidx.core.content.ContextCompat.getColor
49 import androidx.core.content.res.ResourcesCompat
50 import androidx.fragment.app.Fragment
51 import androidx.preference.PreferenceManager
52
53 import com.stoutner.privacybrowser.R
54 import com.stoutner.privacybrowser.activities.DOMAINS_CUSTOM_USER_AGENT
55 import com.stoutner.privacybrowser.activities.DOMAINS_SYSTEM_DEFAULT_USER_AGENT
56 import com.stoutner.privacybrowser.activities.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT
57 import com.stoutner.privacybrowser.activities.SETTINGS_CUSTOM_USER_AGENT
58 import com.stoutner.privacybrowser.activities.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT
59 import com.stoutner.privacybrowser.activities.UNRECOGNIZED_USER_AGENT
60 import com.stoutner.privacybrowser.activities.DomainsActivity
61 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper
62
63 import java.lang.IndexOutOfBoundsException
64 import java.text.DateFormat
65 import java.util.Calendar
66 import java.util.Date
67
68 class DomainSettingsFragment : Fragment() {
69     // Define the class variables.
70     private var scrollY = 0
71
72     companion object {
73         // Define the public constants.
74         const val DATABASE_ID = "database_id"
75         const val SCROLL_Y = "scroll_y"
76
77         // Define the public variables.  `databaseId` is public so it can be accessed from `DomainsActivity`.
78         var databaseId = 0
79     }
80
81     override fun onCreate(savedInstanceState: Bundle?) {
82         // Run the default commands.
83         super.onCreate(savedInstanceState)
84
85         // Store the arguments in class variables.
86         databaseId = requireArguments().getInt(DATABASE_ID)
87         scrollY = requireArguments().getInt(SCROLL_Y)
88     }
89
90     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
91         // Inflate the layout.  The fragment will take care of attaching the root automatically.
92         val domainSettingsView = inflater.inflate(R.layout.domain_settings_fragment, container, false)
93
94         // Get the current theme status.
95         val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
96
97         // Get a handle for the shared preference.
98         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
99
100         // Store the default settings.
101         val defaultUserAgentName = sharedPreferences.getString(getString(R.string.user_agent_key), getString(R.string.user_agent_default_value))
102         val defaultCustomUserAgentString = sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value))
103         val defaultFontSizeString = sharedPreferences.getString(getString(R.string.font_size_key), getString(R.string.font_size_default_value))
104         val defaultSwipeToRefresh = sharedPreferences.getBoolean(getString(R.string.swipe_to_refresh_key), true)
105         val defaultWebViewTheme = sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value))
106         val defaultWideViewport = sharedPreferences.getBoolean(getString(R.string.wide_viewport_key), true)
107         val defaultDisplayWebpageImages = sharedPreferences.getBoolean(getString(R.string.display_webpage_images_key), true)
108
109         // Get handles for the views.
110         val domainSettingsScrollView = domainSettingsView.findViewById<ScrollView>(R.id.domain_settings_scrollview)
111         val domainNameEditText = domainSettingsView.findViewById<EditText>(R.id.domain_settings_name_edittext)
112         val javaScriptImageView = domainSettingsView.findViewById<ImageView>(R.id.javascript_imageview)
113         val javaScriptSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.javascript_switch)
114         val cookiesImageView = domainSettingsView.findViewById<ImageView>(R.id.cookies_imageview)
115         val cookiesSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.cookies_switch)
116         val domStorageImageView = domainSettingsView.findViewById<ImageView>(R.id.dom_storage_imageview)
117         val domStorageSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.dom_storage_switch)
118         val formDataImageView = domainSettingsView.findViewById<ImageView>(R.id.form_data_imageview) // The form data views can be remove once the minimum API >= 26.
119         val formDataSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.form_data_switch) // The form data views can be remove once the minimum API >= 26.
120         val easyListImageView = domainSettingsView.findViewById<ImageView>(R.id.easylist_imageview)
121         val easyListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.easylist_switch)
122         val easyPrivacyImageView = domainSettingsView.findViewById<ImageView>(R.id.easyprivacy_imageview)
123         val easyPrivacySwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.easyprivacy_switch)
124         val fanboysAnnoyanceListImageView = domainSettingsView.findViewById<ImageView>(R.id.fanboys_annoyance_list_imageview)
125         val fanboysAnnoyanceListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.fanboys_annoyance_list_switch)
126         val fanboysSocialBlockingListImageView = domainSettingsView.findViewById<ImageView>(R.id.fanboys_social_blocking_list_imageview)
127         val fanboysSocialBlockingListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.fanboys_social_blocking_list_switch)
128         val ultraListImageView = domainSettingsView.findViewById<ImageView>(R.id.ultralist_imageview)
129         val ultraListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.ultralist_switch)
130         val ultraPrivacyImageView = domainSettingsView.findViewById<ImageView>(R.id.ultraprivacy_imageview)
131         val ultraPrivacySwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.ultraprivacy_switch)
132         val blockAllThirdPartyRequestsImageView = domainSettingsView.findViewById<ImageView>(R.id.block_all_third_party_requests_imageview)
133         val blockAllThirdPartyRequestsSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.block_all_third_party_requests_switch)
134         val userAgentLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.user_agent_linearlayout)
135         val userAgentSpinner = domainSettingsView.findViewById<Spinner>(R.id.user_agent_spinner)
136         val userAgentTextView = domainSettingsView.findViewById<TextView>(R.id.user_agent_textview)
137         val customUserAgentEditText = domainSettingsView.findViewById<EditText>(R.id.custom_user_agent_edittext)
138         val fontSizeLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.font_size_linearlayout)
139         val fontSizeSpinner = domainSettingsView.findViewById<Spinner>(R.id.font_size_spinner)
140         val defaultFontSizeTextView = domainSettingsView.findViewById<TextView>(R.id.default_font_size_textview)
141         val customFontSizeEditText = domainSettingsView.findViewById<EditText>(R.id.custom_font_size_edittext)
142         val swipeToRefreshLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.swipe_to_refresh_linearlayout)
143         val swipeToRefreshImageView = domainSettingsView.findViewById<ImageView>(R.id.swipe_to_refresh_imageview)
144         val swipeToRefreshSpinner = domainSettingsView.findViewById<Spinner>(R.id.swipe_to_refresh_spinner)
145         val swipeToRefreshTextView = domainSettingsView.findViewById<TextView>(R.id.swipe_to_refresh_textview)
146         val webViewThemeLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.webview_theme_linearlayout)
147         val webViewThemeImageView = domainSettingsView.findViewById<ImageView>(R.id.webview_theme_imageview)
148         val webViewThemeSpinner = domainSettingsView.findViewById<Spinner>(R.id.webview_theme_spinner)
149         val webViewThemeTextView = domainSettingsView.findViewById<TextView>(R.id.webview_theme_textview)
150         val wideViewportLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.wide_viewport_linearlayout)
151         val wideViewportImageView = domainSettingsView.findViewById<ImageView>(R.id.wide_viewport_imageview)
152         val wideViewportSpinner = domainSettingsView.findViewById<Spinner>(R.id.wide_viewport_spinner)
153         val wideViewportTextView = domainSettingsView.findViewById<TextView>(R.id.wide_viewport_textview)
154         val displayImagesLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.display_images_linearlayout)
155         val displayImagesImageView = domainSettingsView.findViewById<ImageView>(R.id.display_images_imageview)
156         val displayImagesSpinner = domainSettingsView.findViewById<Spinner>(R.id.display_images_spinner)
157         val displayImagesTextView = domainSettingsView.findViewById<TextView>(R.id.display_images_textview)
158         val pinnedSslCertificateImageView = domainSettingsView.findViewById<ImageView>(R.id.pinned_ssl_certificate_imageview)
159         val pinnedSslCertificateSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.pinned_ssl_certificate_switch)
160         val savedSslCardView = domainSettingsView.findViewById<CardView>(R.id.saved_ssl_certificate_cardview)
161         val savedSslCertificateLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.saved_ssl_certificate_linearlayout)
162         val savedSslCertificateRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.saved_ssl_certificate_radiobutton)
163         val savedSslIssuedToCNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_cname)
164         val savedSslIssuedToONameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_oname)
165         val savedSslIssuedToUNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_uname)
166         val savedSslIssuedByCNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_cname)
167         val savedSslIssuedByONameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_oname)
168         val savedSslIssuedByUNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_uname)
169         val savedSslStartDateTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_start_date)
170         val savedSslEndDateTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_end_date)
171         val currentSslCardView = domainSettingsView.findViewById<CardView>(R.id.current_website_certificate_cardview)
172         val currentWebsiteCertificateLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.current_website_certificate_linearlayout)
173         val currentWebsiteCertificateRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.current_website_certificate_radiobutton)
174         val currentSslIssuedToCNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_cname)
175         val currentSslIssuedToONameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_oname)
176         val currentSslIssuedToUNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_uname)
177         val currentSslIssuedByCNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_cname)
178         val currentSslIssuedByONameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_oname)
179         val currentSslIssuedByUNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_uname)
180         val currentSslStartDateTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_start_date)
181         val currentSslEndDateTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_end_date)
182         val noCurrentWebsiteCertificateTextView = domainSettingsView.findViewById<TextView>(R.id.no_current_website_certificate)
183         val pinnedIpAddressesImageView = domainSettingsView.findViewById<ImageView>(R.id.pinned_ip_addresses_imageview)
184         val pinnedIpAddressesSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.pinned_ip_addresses_switch)
185         val savedIpAddressesCardView = domainSettingsView.findViewById<CardView>(R.id.saved_ip_addresses_cardview)
186         val savedIpAddressesLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.saved_ip_addresses_linearlayout)
187         val savedIpAddressesRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.saved_ip_addresses_radiobutton)
188         val savedIpAddressesTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ip_addresses_textview)
189         val currentIpAddressesCardView = domainSettingsView.findViewById<CardView>(R.id.current_ip_addresses_cardview)
190         val currentIpAddressesLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.current_ip_addresses_linearlayout)
191         val currentIpAddressesRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.current_ip_addresses_radiobutton)
192         val currentIpAddressesTextView = domainSettingsView.findViewById<TextView>(R.id.current_ip_addresses_textview)
193
194         // Hide the WebView theme linear layout if the API < 29.
195         if (Build.VERSION.SDK_INT < 29)
196             webViewThemeLinearLayout.visibility = View.GONE
197
198         // Setup the pinned labels.
199         val cNameLabel = getString(R.string.common_name)
200         val oNameLabel = getString(R.string.organization)
201         val uNameLabel = getString(R.string.organizational_unit)
202         val startDateLabel = getString(R.string.start_date)
203         val endDateLabel = getString(R.string.end_date)
204
205         // Initialize the database handler.
206         val domainsDatabaseHelper = DomainsDatabaseHelper(requireContext())
207
208         // Get the database cursor for this ID.
209         val domainCursor = domainsDatabaseHelper.getCursorForId(databaseId)
210
211         // Move to the first row.
212         domainCursor.moveToFirst()
213
214         // Save the cursor entries as variables.
215         val domainNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME))
216         val javaScriptInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT))
217         val cookiesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES))
218         val domStorageInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE))
219         val formDataInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA)) // Form data can be remove once the minimum API >= 26.
220         val easyListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST))
221         val easyPrivacyInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY))
222         val fanboysAnnoyanceListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST))
223         val fanboysSocialBlockingListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST))
224         val ultraListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST))
225         val ultraPrivacyInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY))
226         val blockAllThirdPartyRequestsInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS))
227         val currentUserAgentName = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT))
228         val fontSizeInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE))
229         val swipeToRefreshInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH))
230         val webViewThemeInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME))
231         val wideViewportInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WIDE_VIEWPORT))
232         val displayImagesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DISPLAY_IMAGES))
233         val pinnedSslCertificateInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE))
234         val savedSslIssuedToCNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME))
235         val savedSslIssuedToONameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION))
236         val savedSslIssuedToUNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT))
237         val savedSslIssuedByCNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME))
238         val savedSslIssuedByONameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION))
239         val savedSslIssuedByUNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT))
240         val pinnedIpAddressesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_IP_ADDRESSES))
241         val savedIpAddresses = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.IP_ADDRESSES))
242
243         // Get the SSL dates from the database.
244         val savedSslStartDateLong = domainCursor.getLong(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_START_DATE))
245         val savedSslEndDateLong = domainCursor.getLong(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_END_DATE))
246
247         // Initialize the saved SSL certificate date variables.
248         var savedSslStartDate: Date? = null
249         var savedSslEndDate: Date? = null
250
251         // Only get the saved SSL certificate dates from the cursor if they are not set to `0`.
252         if (savedSslStartDateLong != 0L)
253             savedSslStartDate = Date(savedSslStartDateLong)
254         if (savedSslEndDateLong != 0L)
255             savedSslEndDate = Date(savedSslEndDateLong)
256
257         // Create array adapters for the spinners.
258         val translatedUserAgentArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.translated_domain_settings_user_agent_names, R.layout.spinner_item)
259         val fontSizeArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.font_size_array, R.layout.spinner_item)
260         val swipeToRefreshArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.swipe_to_refresh_array, R.layout.spinner_item)
261         val webViewThemeArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.webview_theme_array, R.layout.spinner_item)
262         val wideViewportArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.wide_viewport_array, R.layout.spinner_item)
263         val displayImagesArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.display_webpage_images_array, R.layout.spinner_item)
264
265         // Set the drop down view resource on the spinners.
266         translatedUserAgentArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
267         fontSizeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
268         swipeToRefreshArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
269         webViewThemeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
270         wideViewportArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
271         displayImagesArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
272
273         // Set the array adapters for the spinners.
274         userAgentSpinner.adapter = translatedUserAgentArrayAdapter
275         fontSizeSpinner.adapter = fontSizeArrayAdapter
276         swipeToRefreshSpinner.adapter = swipeToRefreshArrayAdapter
277         webViewThemeSpinner.adapter = webViewThemeArrayAdapter
278         wideViewportSpinner.adapter = wideViewportArrayAdapter
279         displayImagesSpinner.adapter = displayImagesArrayAdapter
280
281         // Create a spannable string builder for each TextView that needs multiple colors of text.
282         val savedSslIssuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString)
283         val savedSslIssuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + savedSslIssuedToONameString)
284         val savedSslIssuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + savedSslIssuedToUNameString)
285         val savedSslIssuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedByCNameString)
286         val savedSslIssuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + savedSslIssuedByONameString)
287         val savedSslIssuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + savedSslIssuedByUNameString)
288
289         // Create the date spannable string builders.
290         val savedSslStartDateStringBuilder: SpannableStringBuilder = if (savedSslStartDate == null)
291             SpannableStringBuilder(startDateLabel)
292         else
293             SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslStartDate))
294
295         val savedSslEndDateStringBuilder: SpannableStringBuilder = if (savedSslEndDate == null)
296             SpannableStringBuilder(endDateLabel)
297         else
298             SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslEndDate))
299
300         // Create the color spans.
301         val blueColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.alt_blue_text))
302         val redColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.red_text))
303
304         // Set the domain name from the the database cursor.
305         domainNameEditText.setText(domainNameString)
306
307         // Update the certificates' Common Name color when the domain name text changes.
308         domainNameEditText.addTextChangedListener(object : TextWatcher {
309             override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
310                 // Do nothing.
311             }
312
313             override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
314                 // Do nothing.
315             }
316
317             override fun afterTextChanged(s: Editable) {
318                 // Get the new domain name.
319                 val newDomainName = domainNameEditText.text.toString()
320
321                 // Check the saved SSL certificate against the new domain name.
322                 val savedSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, savedSslIssuedToCNameString)
323
324                 // Create a spannable string builder for the saved certificate's Common Name.
325                 val savedSslCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString)
326
327                 // Format the saved certificate's Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
328                 if (savedSslMatchesNewDomainName) {
329                     savedSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
330                 } else {
331                     savedSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, savedSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
332                 }
333
334                 // Update the saved SSL issued to CName text view.
335                 savedSslIssuedToCNameTextView.text = savedSslCNameStringBuilder
336
337                 // Update the current website certificate if it exists.
338                 if (DomainsActivity.sslIssuedToCName != null) {
339                     // Check the current website certificate against the new domain name.
340                     val currentSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, DomainsActivity.sslIssuedToCName)
341
342                     // Create a spannable string builder for the current website certificate's Common Name.
343                     val currentSslCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName)
344
345                     // Format the current certificate Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
346                     if (currentSslMatchesNewDomainName) {
347                         currentSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
348                     } else {
349                         currentSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, currentSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
350                     }
351
352                     // Update the current SSL issued to CName text view.
353                     currentSslIssuedToCNameTextView.text = currentSslCNameStringBuilder
354                 }
355             }
356         })
357
358         // Set the switch positions.
359         javaScriptSwitch.isChecked = (javaScriptInt == 1)
360         cookiesSwitch.isChecked = (cookiesInt == 1)
361         domStorageSwitch.isChecked = (domStorageInt == 1)
362         formDataSwitch.isChecked = (formDataInt == 1)  // Form data can be removed once the minimum API >= 26.
363         easyListSwitch.isChecked = (easyListInt == 1)
364         easyPrivacySwitch.isChecked = (easyPrivacyInt == 1)
365         fanboysAnnoyanceListSwitch.isChecked = (fanboysAnnoyanceListInt == 1)
366         fanboysSocialBlockingListSwitch.isChecked = (fanboysSocialBlockingListInt == 1)
367         ultraListSwitch.isChecked = (ultraListInt == 1)
368         ultraPrivacySwitch.isChecked = (ultraPrivacyInt == 1)
369         blockAllThirdPartyRequestsSwitch.isChecked = (blockAllThirdPartyRequestsInt == 1)
370         pinnedSslCertificateSwitch.isChecked = (pinnedSslCertificateInt == 1)
371         pinnedIpAddressesSwitch.isChecked = (pinnedIpAddressesInt == 1)
372
373         // Set the switch icon colors.
374         cookiesImageView.isSelected = (cookiesInt == 1)
375         domStorageImageView.isSelected = (domStorageInt == 1)
376         formDataImageView.isSelected = (formDataInt == 1)  // Form data can be removed once the minimum API >= 26.
377         easyListImageView.isSelected = (easyListInt == 1)
378         easyPrivacyImageView.isSelected = (easyPrivacyInt == 1)
379         fanboysAnnoyanceListImageView.isSelected = (fanboysAnnoyanceListInt == 1)
380         fanboysSocialBlockingListImageView.isSelected = (fanboysSocialBlockingListInt == 1)
381         ultraListImageView.isSelected = (ultraListInt == 1)
382         ultraPrivacyImageView.isSelected = (ultraPrivacyInt == 1)
383         blockAllThirdPartyRequestsImageView.isSelected = (blockAllThirdPartyRequestsInt == 1)
384         pinnedSslCertificateImageView.isSelected = (pinnedSslCertificateInt == 1)
385         pinnedIpAddressesImageView.isSelected = (pinnedIpAddressesInt == 1)
386
387         // Set the JavaScript icon.
388         if (javaScriptInt == 1)
389             javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null))
390         else
391             javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null))
392
393         // Set the DOM storage switch status based on the JavaScript status.
394         domStorageSwitch.isEnabled = (javaScriptInt == 1)
395
396         // Set the DOM storage icon ghosted status based on the JavaScript status.
397         domStorageImageView.isEnabled = (javaScriptInt == 1)
398
399         // Set the form data visibility.  Form data can be removed once the minimum API >= 26.
400         if (Build.VERSION.SDK_INT >= 26) {
401             // Hide the form data image view and switch.
402             formDataImageView.visibility = View.GONE
403             formDataSwitch.visibility = View.GONE
404         }
405
406         // Set Fanboy's Social Blocking List switch status based on the Annoyance List status.
407         fanboysSocialBlockingListSwitch.isEnabled = (fanboysAnnoyanceListInt == 0)
408
409         // Set the Social Blocking List icon ghosted status based on the Annoyance List status.
410         fanboysSocialBlockingListImageView.isEnabled = (fanboysAnnoyanceListInt == 0)
411
412         // Open the spinners when the text view is clicked.
413         userAgentTextView.setOnClickListener { userAgentSpinner.performClick() }
414         defaultFontSizeTextView.setOnClickListener { fontSizeSpinner.performClick() }
415         swipeToRefreshTextView.setOnClickListener { swipeToRefreshSpinner.performClick() }
416         webViewThemeTextView.setOnClickListener { webViewThemeSpinner.performClick() }
417         wideViewportTextView.setOnClickListener { wideViewportSpinner.performClick() }
418         displayImagesTextView.setOnClickListener { displayImagesSpinner.performClick() }
419
420         // Inflated a WebView to get the default user agent.
421         // `@SuppressLint("InflateParams")` removes the warning about using `null` as the `ViewGroup`, which in this case makes sense because the bare WebView should not be displayed on the screen.
422         @SuppressLint("InflateParams") val bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false)
423         val bareWebView = bareWebViewLayout.findViewById<WebView>(R.id.bare_webview)
424         val webViewDefaultUserAgentString = bareWebView.settings.userAgentString
425
426         // Get a handle for the user agent array adapter.  This array does not contain the `System default` entry.
427         val userAgentNamesArray = ArrayAdapter.createFromResource(requireContext(), R.array.user_agent_names, R.layout.spinner_item)
428
429         // Get the positions of the user agent and the default user agent.
430         val userAgentArrayPosition = userAgentNamesArray.getPosition(currentUserAgentName)
431         val defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName)
432
433         // Get a handle for the user agent data array.  This array does not contain the `System default` entry.
434         val userAgentDataArray = resources.getStringArray(R.array.user_agent_data)
435
436         // Set the user agent text.
437         if (currentUserAgentName == getString(R.string.system_default_user_agent)) {  // Use the system default user agent.
438             // Set the user agent according to the system default.
439             when (defaultUserAgentArrayPosition) {
440                 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
441                 UNRECOGNIZED_USER_AGENT -> userAgentTextView.text = defaultUserAgentName
442
443                 // Display the WebView default user agent.
444                 SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentTextView.text = webViewDefaultUserAgentString
445
446                 // Display the custom user agent.
447                 SETTINGS_CUSTOM_USER_AGENT -> userAgentTextView.text = defaultCustomUserAgentString
448
449                 // Get the user agent string from the user agent data array.
450                 else -> userAgentTextView.text = userAgentDataArray[defaultUserAgentArrayPosition]
451             }
452
453             // Set the background color to be transparent.
454             userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
455         } else if (userAgentArrayPosition == UNRECOGNIZED_USER_AGENT || currentUserAgentName == getString(R.string.custom_user_agent)) {
456             // A custom user agent is stored in the current user agent name.  The second check is necessary in case the user did not change the default custom text.
457             // Set the user agent spinner to `Custom user agent`.
458             userAgentSpinner.setSelection(DOMAINS_CUSTOM_USER_AGENT)
459
460             // Hide the user agent text view.
461             userAgentTextView.visibility = View.GONE
462
463             // Show the custom user agent edit text and set the current user agent name as the text.
464             customUserAgentEditText.visibility = View.VISIBLE
465             customUserAgentEditText.setText(currentUserAgentName)
466
467             // Set the background color to be blue.
468             userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
469         } else {  // The user agent name contains one of the canonical user agents.
470             // Set the user agent spinner selection.  The spinner has one more entry at the beginning than the user agent data array, so the position must be incremented.
471             userAgentSpinner.setSelection(userAgentArrayPosition + 1)
472
473             // Show the user agent text view.
474             userAgentTextView.visibility = View.VISIBLE
475
476             // Hide the custom user agent edit text.
477             customUserAgentEditText.visibility = View.GONE
478
479             // Set the user agent text.
480             if (userAgentArrayPosition == DOMAINS_WEBVIEW_DEFAULT_USER_AGENT) {  // The WebView default user agent is selected.
481                 // Display the WebView default user agent.
482                 userAgentTextView.text = webViewDefaultUserAgentString
483             } else {  // A user agent besides the default is selected.
484                 // Get the user agent string from the user agent data array.  The spinner has one more entry at the beginning than the user agent data array, so the position must be incremented.
485                 userAgentTextView.text = userAgentDataArray[userAgentArrayPosition + 1]
486             }
487
488             // Set the background color to be blue.
489             userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
490         }
491
492
493         // Display the font size settings.
494         if (fontSizeInt == 0) {  // `0` is the code for system default font size.
495             // Set the font size to the system default.
496             fontSizeSpinner.setSelection(0)
497
498             // Show the default font size text view.
499             defaultFontSizeTextView.visibility = View.VISIBLE
500
501             // Hide the custom font size edit text.
502             customFontSizeEditText.visibility = View.GONE
503
504             // Set the default font size as the text of the custom font size edit text.  This way, if the user switches to custom it will already be populated.
505             customFontSizeEditText.setText(defaultFontSizeString)
506
507             // Set the background color to be transparent.
508             fontSizeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
509         } else {  // A custom font size is selected.
510             // Set the spinner to the custom font size.
511             fontSizeSpinner.setSelection(1)
512
513             // Hide the default font size text view.
514             defaultFontSizeTextView.visibility = View.GONE
515
516             // Show the custom font size edit text.
517             customFontSizeEditText.visibility = View.GONE
518
519             // Set the custom font size.
520             customFontSizeEditText.setText(fontSizeInt.toString())
521
522             // Set the background color to be blue.
523             fontSizeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
524         }
525
526         // Initialize the default font size percentage string.
527         val defaultFontSizePercentageString = "$defaultFontSizeString%"
528
529         // Set the default font size text in the text view.
530         defaultFontSizeTextView.text = defaultFontSizePercentageString
531
532         // Select the swipe-to-refresh selection in the spinner.
533         swipeToRefreshSpinner.setSelection(swipeToRefreshInt)
534
535         // Set the swipe-to-refresh text.
536         if (defaultSwipeToRefresh)
537             swipeToRefreshTextView.text = swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
538         else
539             swipeToRefreshTextView.text = swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
540
541         // Set the swipe-to-refresh icon and text view settings.
542         when (swipeToRefreshInt) {
543             DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
544                 // Set the icon color.
545                 swipeToRefreshImageView.isSelected = defaultSwipeToRefresh
546
547                 // Show the swipe-to-refresh text view.
548                 swipeToRefreshTextView.visibility = View.VISIBLE
549
550                 // Set the background color to be transparent.
551                 swipeToRefreshLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
552             }
553
554             DomainsDatabaseHelper.ENABLED -> {
555                 // Set the icon color.
556                 swipeToRefreshImageView.isSelected = true
557
558                 // Hide the swipe-to-refresh text view.
559                 swipeToRefreshTextView.visibility = View.GONE
560
561                 // Set the background color to be blue.
562                 swipeToRefreshLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
563             }
564
565             DomainsDatabaseHelper.DISABLED -> {
566                 // Set the icon color.
567                 swipeToRefreshImageView.isSelected = false
568
569                 // Hide the swipe-to-refresh text view.
570                 swipeToRefreshTextView.visibility = View.GONE
571
572                 // Set the background color to be blue.
573                 swipeToRefreshLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
574             }
575         }
576
577         // Get the WebView theme string arrays.
578         val webViewThemeStringArray = resources.getStringArray(R.array.webview_theme_array)
579         val webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values)
580
581         // Get the WebView theme entry number that matches the current WebView theme.
582         val appWebViewThemeEntryNumber = when (defaultWebViewTheme) {
583             webViewThemeEntryValuesStringArray[1] -> { 1 }  // The light theme is selected.
584             webViewThemeEntryValuesStringArray[2] -> { 2 }  // The dark theme is selected.
585             else -> { 0 }  // The system default theme is selected.
586         }
587
588         // Select the WebView theme in the spinner.
589         webViewThemeSpinner.setSelection(webViewThemeInt)
590
591         // Set the WebView theme text.
592         if (appWebViewThemeEntryNumber == DomainsDatabaseHelper.SYSTEM_DEFAULT) {  // The app WebView theme is system default.
593             // Set the text according to the current UI theme.
594             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
595                 webViewThemeTextView.text = webViewThemeStringArray[DomainsDatabaseHelper.LIGHT_THEME]
596             else
597                 webViewThemeTextView.text = webViewThemeStringArray[DomainsDatabaseHelper.DARK_THEME]
598         } else {  // The app WebView theme is not system default.
599             // Set the text according to the app WebView theme.
600             webViewThemeTextView.text = webViewThemeStringArray[appWebViewThemeEntryNumber]
601         }
602
603         // Set the WebView theme icon and text visibility.
604         when (webViewThemeInt) {
605             DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
606                 // Set the icon color.
607                 when (appWebViewThemeEntryNumber) {
608                     DomainsDatabaseHelper.SYSTEM_DEFAULT -> webViewThemeImageView.isSelected = (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
609                     DomainsDatabaseHelper.LIGHT_THEME -> webViewThemeImageView.isSelected = true
610                     DomainsDatabaseHelper.DARK_THEME -> webViewThemeImageView.isSelected = false
611                 }
612
613                 // Show the WebView theme text view.
614                 webViewThemeTextView.visibility = View.VISIBLE
615
616                 // Set the background color to be transparent.
617                 webViewThemeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
618             }
619
620             DomainsDatabaseHelper.LIGHT_THEME -> {
621                 // Set the icon color.
622                 webViewThemeImageView.isSelected = true
623
624                 // Hide the WebView theme text view.
625                 webViewThemeTextView.visibility = View.GONE
626
627                 // Set the background color to be blue.
628                 webViewThemeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
629             }
630
631             DomainsDatabaseHelper.DARK_THEME -> {
632                 // Set the icon color.
633                 webViewThemeImageView.isSelected = false
634
635                 // Hide the WebView theme text view.
636                 webViewThemeTextView.visibility = View.GONE
637
638                 // Set the background color to be blue.
639                 webViewThemeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
640             }
641         }
642
643         // Select the wide viewport in the spinner.
644         wideViewportSpinner.setSelection(wideViewportInt)
645
646         // Set the default wide viewport text.
647         if (defaultWideViewport)
648             wideViewportTextView.text = wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
649         else
650             wideViewportTextView.text = wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
651
652         // Set the wide viewport icon and text view settings.
653         when (wideViewportInt) {
654             DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
655                 // Set the icon color.
656                 wideViewportImageView.isSelected = defaultWideViewport
657
658                 // Show the wide viewport text view.
659                 wideViewportTextView.visibility = View.VISIBLE
660
661                 // Set the background color to be transparent.
662                 wideViewportLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
663             }
664
665             DomainsDatabaseHelper.ENABLED -> {
666                 // Set the icon color.
667                 wideViewportImageView.isSelected = true
668
669                 // Hide the wide viewport text view.
670                 wideViewportTextView.visibility = View.GONE
671
672                 // Set the background color to be blue.
673                 wideViewportLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
674             }
675
676             DomainsDatabaseHelper.DISABLED -> {
677                 // Set the icon color.
678                 wideViewportImageView.isSelected = false
679
680                 // Hide the wide viewport text view.
681                 wideViewportTextView.visibility = View.GONE
682
683                 // Set the background color to be blue.
684                 wideViewportLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
685             }
686         }
687
688         // Display the website images mode in the spinner.
689         displayImagesSpinner.setSelection(displayImagesInt)
690
691         // Set the default display images text.
692         if (defaultDisplayWebpageImages)
693             displayImagesTextView.text = displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
694         else
695             displayImagesTextView.text = displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
696
697         // Set the display website images icon and text view settings.
698         when (displayImagesInt) {
699             DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
700                 // Set the icon color.
701                 displayImagesImageView.isSelected = defaultDisplayWebpageImages
702
703                 // Show the display images text view.
704                 displayImagesTextView.visibility = View.VISIBLE
705
706                 // Set the background color to be transparent.
707                 displayImagesLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
708             }
709
710             DomainsDatabaseHelper.ENABLED -> {
711                 // Set the icon color.
712                 displayImagesImageView.isSelected = true
713
714                 // Hide the display images text view.
715                 displayImagesTextView.visibility = View.GONE
716
717                 // Set the background color to be blue.
718                 displayImagesLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
719             }
720
721             DomainsDatabaseHelper.DISABLED -> {
722                 // Set the icon color.
723                 displayImagesImageView.isSelected = false
724
725                 // Hide the display images text view.
726                 displayImagesTextView.visibility = View.GONE
727
728                 // Set the background color to be blue.
729                displayImagesLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
730             }
731         }
732
733         // Store the current date.
734         val currentDate = Calendar.getInstance().time
735
736         // Setup the string builders to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
737         savedSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, savedSslIssuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
738         savedSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, savedSslIssuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
739         savedSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslIssuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
740         savedSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, savedSslIssuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
741         savedSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, savedSslIssuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
742
743         // Check the certificate Common Name against the domain name.
744         val savedSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslIssuedToCNameString)
745
746         // Format the issued to Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
747         if (savedSslCommonNameMatchesDomainName)
748             savedSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
749         else
750             savedSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, savedSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
751
752         //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
753         if (savedSslStartDate != null && savedSslStartDate.after(currentDate))  // The certificate start date is in the future.
754             savedSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, savedSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
755         else  // The certificate start date is in the past.
756             savedSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, savedSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
757
758         // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
759         if (savedSslEndDate != null && savedSslEndDate.before(currentDate))  // The certificate end date is in the past.
760             savedSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, savedSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
761         else  // The certificate end date is in the future.
762             savedSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, savedSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
763
764         // Display the saved website SSL certificate strings.
765         savedSslIssuedToCNameTextView.text = savedSslIssuedToCNameStringBuilder
766         savedSslIssuedToONameTextView.text = savedSslIssuedToONameStringBuilder
767         savedSslIssuedToUNameTextView.text = savedSslIssuedToUNameStringBuilder
768         savedSslIssuedByCNameTextView.text = savedSslIssuedByCNameStringBuilder
769         savedSslIssuedByONameTextView.text = savedSslIssuedByONameStringBuilder
770         savedSslIssuedByUNameTextView.text = savedSslIssuedByUNameStringBuilder
771         savedSslStartDateTextView.text = savedSslStartDateStringBuilder
772         savedSslEndDateTextView.text = savedSslEndDateStringBuilder
773
774         // Populate the current website SSL certificate if there is one.
775         if (DomainsActivity.sslIssuedToCName != null) {
776             // Get dates from the raw long values.
777             val currentSslStartDate = Date(DomainsActivity.sslStartDateLong)
778             val currentSslEndDate = Date(DomainsActivity.sslEndDateLong)
779
780             // Create a spannable string builder for each text view that needs multiple colors of text.
781             val currentSslIssuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName)
782             val currentSslIssuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedToOName)
783             val currentSslIssuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedToUName)
784             val currentSslIssuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedByCName)
785             val currentSslIssuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedByOName)
786             val currentSslIssuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedByUName)
787             val currentSslStartDateStringBuilder = SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslStartDate))
788             val currentSslEndDateStringBuilder = SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslEndDate))
789
790             // Setup the string builders to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
791             currentSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, currentSslIssuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
792             currentSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, currentSslIssuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
793             currentSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslIssuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
794             currentSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, currentSslIssuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
795             currentSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, currentSslIssuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
796
797             // Check the certificate Common Name against the domain name.
798             val currentSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, DomainsActivity.sslIssuedToCName)
799
800             // Format the issued to Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
801             if (currentSslCommonNameMatchesDomainName)
802                 currentSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
803             else
804                 currentSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, currentSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
805
806             //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
807             if (currentSslStartDate.after(currentDate))  // The certificate start date is in the future.
808                 currentSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, currentSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
809             else  // The certificate start date is in the past.
810                 currentSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, currentSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
811
812             // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
813             if (currentSslEndDate.before(currentDate))  // The certificate end date is in the past.
814                 currentSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, currentSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
815             else  // The certificate end date is in the future.
816                 currentSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, currentSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
817
818             // Display the current website SSL certificate strings.
819             currentSslIssuedToCNameTextView.text = currentSslIssuedToCNameStringBuilder
820             currentSslIssuedToONameTextView.text = currentSslIssuedToONameStringBuilder
821             currentSslIssuedToUNameTextView.text = currentSslIssuedToUNameStringBuilder
822             currentSslIssuedByCNameTextView.text = currentSslIssuedByCNameStringBuilder
823             currentSslIssuedByONameTextView.text = currentSslIssuedByONameStringBuilder
824             currentSslIssuedByUNameTextView.text = currentSslIssuedByUNameStringBuilder
825             currentSslStartDateTextView.text = currentSslStartDateStringBuilder
826             currentSslEndDateTextView.text = currentSslEndDateStringBuilder
827         }
828
829         // Set the initial display status of the SSL certificates card views.
830         if (pinnedSslCertificateSwitch.isChecked) {  // An SSL certificate is pinned.
831             // Set the visibility of the saved SSL certificate.
832             if (savedSslIssuedToCNameString == null)
833                 savedSslCardView.visibility = View.GONE
834             else
835                 savedSslCardView.visibility = View.VISIBLE
836
837             // Set the visibility of the current website SSL certificate.
838             if (DomainsActivity.sslIssuedToCName == null) {  // There is no current SSL certificate.
839                 // Hide the SSL certificate.
840                 currentSslCardView.visibility = View.GONE
841
842                 // Show the instruction.
843                 noCurrentWebsiteCertificateTextView.visibility = View.VISIBLE
844             } else {  // There is a current SSL certificate.
845                 // Show the SSL certificate.
846                 currentSslCardView.visibility = View.VISIBLE
847
848                 // Hide the instruction.
849                 noCurrentWebsiteCertificateTextView.visibility = View.GONE
850             }
851
852             // Set the status of the radio buttons and the card view backgrounds.
853             if (savedSslCardView.visibility == View.VISIBLE) {  // The saved SSL certificate is displayed.
854                 // Check the saved SSL certificate radio button.
855                 savedSslCertificateRadioButton.isChecked = true
856
857                 // Uncheck the current website SSL certificate radio button.
858                 currentWebsiteCertificateRadioButton.isChecked = false
859
860                 // Darken the background of the current website SSL certificate linear layout.
861                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
862             } else if (currentSslCardView.visibility == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
863                 // Check the current website SSL certificate radio button.
864                 currentWebsiteCertificateRadioButton.isChecked = true
865
866                 // Uncheck the saved SSL certificate radio button.
867                 savedSslCertificateRadioButton.isChecked = false
868             } else {  // Neither SSL certificate is visible.
869                 // Uncheck both radio buttons.
870                 savedSslCertificateRadioButton.isChecked = false
871                 currentWebsiteCertificateRadioButton.isChecked = false
872             }
873         } else {  // An SSL certificate is not pinned.
874             // Hide the SSl certificates and instructions.
875             savedSslCardView.visibility = View.GONE
876             currentSslCardView.visibility = View.GONE
877             noCurrentWebsiteCertificateTextView.visibility = View.GONE
878
879             // Uncheck the radio buttons.
880             savedSslCertificateRadioButton.isChecked = false
881             currentWebsiteCertificateRadioButton.isChecked = false
882         }
883
884         // Populate the saved and current IP addresses.
885         savedIpAddressesTextView.text = savedIpAddresses
886         currentIpAddressesTextView.text = DomainsActivity.currentIpAddresses
887
888         // Set the initial display status of the IP addresses card views.
889         if (pinnedIpAddressesSwitch.isChecked) {  // IP addresses are pinned.
890             // Set the visibility of the saved IP addresses.
891             if (savedIpAddresses == null)  // There are no saved IP addresses.
892                 savedIpAddressesCardView.visibility = View.GONE
893             else  // There are saved IP addresses.
894                 savedIpAddressesCardView.visibility = View.VISIBLE
895
896             // Set the visibility of the current IP addresses.
897             currentIpAddressesCardView.visibility = View.VISIBLE
898
899             // Set the status of the radio buttons and the card view backgrounds.
900             if (savedIpAddressesCardView.visibility == View.VISIBLE) {  // The saved IP addresses are displayed.
901                 // Check the saved IP addresses radio button.
902                 savedIpAddressesRadioButton.isChecked = true
903
904                 // Uncheck the current IP addresses radio button.
905                 currentIpAddressesRadioButton.isChecked = false
906
907                 // Darken the background of the current IP addresses linear layout.
908                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
909             } else {  // The saved IP addresses are hidden.
910                 // Check the current IP addresses radio button.
911                 currentIpAddressesRadioButton.isChecked = true
912
913                 // Uncheck the saved IP addresses radio button.
914                 savedIpAddressesRadioButton.isChecked = false
915             }
916         } else {  // IP addresses are not pinned.
917             // Hide the IP addresses card views.
918             savedIpAddressesCardView.visibility = View.GONE
919             currentIpAddressesCardView.visibility = View.GONE
920
921             // Uncheck the radio buttons.
922             savedIpAddressesRadioButton.isChecked = false
923             currentIpAddressesRadioButton.isChecked = false
924         }
925
926
927         // Set the JavaScript switch listener.
928         javaScriptSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
929             // Update the JavaScript icon.
930             if (isChecked)
931                 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null))
932             else
933                 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null))
934
935             // Set the DOM storage switch status.
936             domStorageSwitch.isEnabled = isChecked
937
938             // Set the DOM storage ghosted icon status.
939             domStorageImageView.isEnabled = isChecked
940         }
941
942         // Set the cookies switch listener.
943         cookiesSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
944             // Update the icon color.
945             cookiesImageView.isSelected = isChecked
946         }
947
948         // Set the DOM Storage switch listener.
949         domStorageSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
950             // Update the icon color.
951             domStorageImageView.isSelected = isChecked
952         }
953
954         // Set the form data switch listener.  It can be removed once the minimum API >= 26.
955         if (Build.VERSION.SDK_INT < 26) {
956             formDataSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
957                 // Update the icon color.
958                 formDataImageView.isSelected = isChecked
959             }
960         }
961
962         // Set the EasyList switch listener.
963         easyListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
964             // Update the icon color.
965             easyListImageView.isSelected = isChecked
966         }
967
968         // Set the EasyPrivacy switch listener.
969         easyPrivacySwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
970             // Update the icon color.
971             easyPrivacyImageView.isSelected = isChecked
972         }
973
974         // Set the Fanboy's Annoyance List switch listener.
975         fanboysAnnoyanceListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
976             // Update the icon color.
977             fanboysAnnoyanceListImageView.isSelected = isChecked
978
979             // Set Fanboy's Social Blocking List switch position.
980             fanboysSocialBlockingListSwitch.isEnabled = !isChecked
981
982             // Set the Social Blocking List icon ghosted status.
983             fanboysSocialBlockingListImageView.isEnabled = !isChecked
984         }
985
986         // Set the Fanboy's Social Blocking List switch listener.
987         fanboysSocialBlockingListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
988             // Update the icon color.
989             fanboysSocialBlockingListImageView.isSelected = isChecked
990         }
991
992         // Set the UltraList switch listener.
993         ultraListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
994             // Update the icon color.
995             ultraListImageView.isSelected = isChecked
996         }
997
998         // Set the UltraPrivacy switch listener.
999         ultraPrivacySwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1000             // Update the icon color.
1001             ultraPrivacyImageView.isSelected = isChecked
1002         }
1003
1004         // Set the block all third-party requests switch listener.
1005         blockAllThirdPartyRequestsSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1006             // Update the icon color.
1007             blockAllThirdPartyRequestsImageView.isSelected = isChecked
1008         }
1009
1010         // Set the user agent spinner listener.
1011         userAgentSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1012             override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1013                 // Set the new user agent.
1014                 when (position) {
1015                     DOMAINS_SYSTEM_DEFAULT_USER_AGENT -> {
1016                         // Show the user agent text view.
1017                         userAgentTextView.visibility = View.VISIBLE
1018
1019                         // Hide the custom user agent edit text.
1020                         customUserAgentEditText.visibility = View.GONE
1021
1022                         // Set the user text.
1023                         when (defaultUserAgentArrayPosition) {
1024                             // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
1025                             UNRECOGNIZED_USER_AGENT -> userAgentTextView.text = defaultUserAgentName
1026
1027                             // Display the `WebView` default user agent.
1028                             SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentTextView.text = webViewDefaultUserAgentString
1029
1030                             // Display the custom user agent.
1031                             SETTINGS_CUSTOM_USER_AGENT -> userAgentTextView.text = defaultCustomUserAgentString
1032
1033                             // Get the user agent string from the user agent data array.
1034                             else -> userAgentTextView.text = userAgentDataArray[defaultUserAgentArrayPosition]
1035                         }
1036
1037                         // Set the background color to be transparent.
1038                         userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
1039                     }
1040
1041                     DOMAINS_WEBVIEW_DEFAULT_USER_AGENT -> {
1042                         // Show the user agent text view.
1043                         userAgentTextView.visibility = View.VISIBLE
1044
1045                         // Set the user agent text.
1046                         userAgentTextView.text = webViewDefaultUserAgentString
1047
1048                         // Hide the custom user agent EditTex.
1049                         customUserAgentEditText.visibility = View.GONE
1050
1051                         // Set the background color to be blue.
1052                         userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1053                     }
1054
1055                     DOMAINS_CUSTOM_USER_AGENT -> {
1056                         // Hide the user agent TextView.
1057                         userAgentTextView.visibility = View.GONE
1058
1059                         // Show the custom user agent edit text.
1060                         customUserAgentEditText.visibility = View.VISIBLE
1061
1062                         // Set the current user agent name as the text.
1063                         customUserAgentEditText.setText(currentUserAgentName)
1064
1065                         // Set the background color to be blue.
1066                         userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1067                     }
1068
1069                     else -> {
1070                         // Show the user agent text view.
1071                         userAgentTextView.visibility = View.VISIBLE
1072
1073                         // Set the text from the user agent data array, which has one less entry than the spinner, so the position must be decremented.
1074                         userAgentTextView.text = userAgentDataArray[position - 1]
1075
1076                         // Hide the custom user agent edit text.
1077                         customUserAgentEditText.visibility = View.GONE
1078
1079                         // Set the background color to be blue.
1080                         userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1081                     }
1082                 }
1083             }
1084
1085             override fun onNothingSelected(parent: AdapterView<*>?) {
1086                 // Do nothing.
1087             }
1088         }
1089
1090         // Set the font size spinner listener.
1091         fontSizeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1092             override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1093                 // Update the font size display options.
1094                 if (position == 0) {  // The system default font size has been selected.
1095                     // Show the default font size text view.
1096                     defaultFontSizeTextView.visibility = View.VISIBLE
1097
1098                     // Hide the custom font size edit text.
1099                     customFontSizeEditText.visibility = View.GONE
1100
1101                     // Set the background color to be transparent.
1102                     fontSizeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
1103                 } else {  // A custom font size has been selected.
1104                     // Hide the default font size text view.
1105                     defaultFontSizeTextView.visibility = View.GONE
1106
1107                     // Show the custom font size edit text.
1108                     customFontSizeEditText.visibility = View.VISIBLE
1109
1110                     // Set the background color to be blue.
1111                     fontSizeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1112                 }
1113             }
1114
1115             override fun onNothingSelected(parent: AdapterView<*>?) {
1116                 // Do nothing.
1117             }
1118         }
1119
1120         // Set the swipe-to-refresh spinner listener.
1121         swipeToRefreshSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1122             override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1123                 // Update the icon and the visibility of the text view.
1124                 when (position) {
1125                     DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1126                         // Set the icon color.
1127                         swipeToRefreshImageView.isSelected = defaultSwipeToRefresh
1128
1129                         // Show the swipe-to-refresh text view.
1130                         swipeToRefreshTextView.visibility = View.VISIBLE
1131
1132                         // Set the background color to be transparent.
1133                         swipeToRefreshLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
1134                     }
1135
1136                     DomainsDatabaseHelper.ENABLED -> {
1137                         // Set the icon color.
1138                         swipeToRefreshImageView.isSelected = true
1139
1140                         // Hide the swipe-to-refresh text view.
1141                         swipeToRefreshTextView.visibility = View.GONE
1142
1143                         // Set the background color to be blue.
1144                         swipeToRefreshLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1145                     }
1146
1147                     DomainsDatabaseHelper.DISABLED -> {
1148                         // Set the icon color.
1149                         swipeToRefreshImageView.isSelected = false
1150
1151                         // Hide the swipe-to-refresh text view.
1152                         swipeToRefreshTextView.visibility = View.GONE
1153
1154                         // Set the background color to be blue.
1155                         swipeToRefreshLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1156                     }
1157                 }
1158             }
1159
1160             override fun onNothingSelected(parent: AdapterView<*>?) {
1161                 // Do nothing.
1162             }
1163         }
1164
1165         // Set the WebView theme spinner listener.
1166         webViewThemeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1167             override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1168                 // Update the icon and the visibility of the WebView theme text view.
1169                 when (position) {
1170                     DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1171                         // Set the icon color.
1172                         when (appWebViewThemeEntryNumber) {
1173                             DomainsDatabaseHelper.SYSTEM_DEFAULT -> webViewThemeImageView.isSelected = (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
1174                             DomainsDatabaseHelper.LIGHT_THEME -> webViewThemeImageView.isSelected = true
1175                             DomainsDatabaseHelper.DARK_THEME -> webViewThemeImageView.isSelected = false
1176                         }
1177
1178                         // Show the WebView theme text view.
1179                         webViewThemeTextView.visibility = View.VISIBLE
1180
1181                         // Set the background color to be transparent.
1182                         webViewThemeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
1183                     }
1184
1185                     DomainsDatabaseHelper.LIGHT_THEME -> {
1186                         // Set the icon color.
1187                         webViewThemeImageView.isSelected = true
1188
1189                         // Hide the WebView theme text view.
1190                         webViewThemeTextView.visibility = View.GONE
1191
1192                         // Set the background color to be blue.
1193                         webViewThemeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1194                     }
1195
1196                     DomainsDatabaseHelper.DARK_THEME -> {
1197                         // Set the icon color.
1198                         webViewThemeImageView.isSelected = false
1199
1200                         // Hide the WebView theme text view.
1201                         webViewThemeTextView.visibility = View.GONE
1202
1203                         // Set the background color to be blue.
1204                         webViewThemeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1205                     }
1206                 }
1207             }
1208
1209             override fun onNothingSelected(parent: AdapterView<*>?) {
1210                 // Do nothing.
1211             }
1212         }
1213
1214         // Set the wide viewport spinner listener.
1215         wideViewportSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1216             override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1217                 // Update the icon and the visibility of the wide viewport text view.
1218                 when (position) {
1219                     DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1220                         // Set the icon color.
1221                         wideViewportImageView.isSelected = defaultWideViewport
1222
1223                         // Show the wide viewport text view.
1224                         wideViewportTextView.visibility = View.VISIBLE
1225
1226                         // Set the background color to be transparent.
1227                         wideViewportLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
1228                     }
1229
1230                     DomainsDatabaseHelper.ENABLED -> {
1231                         // Set the icon color.
1232                         wideViewportImageView.isSelected = true
1233
1234                         // Hide the wide viewport text view.
1235                         wideViewportTextView.visibility = View.GONE
1236
1237                         // Set the background color to be blue.
1238                         wideViewportLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1239                     }
1240
1241                     DomainsDatabaseHelper.DISABLED -> {
1242                         // Set the icon color.
1243                         wideViewportImageView.isSelected = false
1244
1245                         // Hid ethe wide viewport text view.
1246                         wideViewportTextView.visibility = View.GONE
1247
1248                         // Set the background color to be blue.
1249                         wideViewportLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1250                     }
1251                 }
1252             }
1253
1254             override fun onNothingSelected(parent: AdapterView<*>?) {
1255                 // Do nothing.
1256             }
1257         }
1258
1259         // Set the display webpage images spinner listener.
1260         displayImagesSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1261             override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1262                 // Update the icon and the visibility of the display images text view.
1263                 when (position) {
1264                     DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1265                         // Set the icon color.
1266                         displayImagesImageView.isSelected = defaultDisplayWebpageImages
1267
1268                         // Show the display images text view.
1269                         displayImagesTextView.visibility = View.VISIBLE
1270
1271                         // Set the background color to be transparent.
1272                         displayImagesLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
1273                     }
1274
1275                     DomainsDatabaseHelper.ENABLED -> {
1276                         // Set the icon color.
1277                         displayImagesImageView.isSelected = true
1278
1279                         // Hide the display images text view.
1280                         displayImagesTextView.visibility = View.GONE
1281
1282                         // Set the background color to be blue.
1283                         displayImagesLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1284                     }
1285
1286                     DomainsDatabaseHelper.DISABLED -> {
1287                         // Set the icon color.
1288                         displayImagesImageView.isSelected = false
1289
1290                         // Hide the display images text view.
1291                         displayImagesTextView.visibility = View.GONE
1292
1293                         // Set the background color to be blue.
1294                         displayImagesLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1295                     }
1296                 }
1297             }
1298
1299             override fun onNothingSelected(parent: AdapterView<*>?) {
1300                 // Do nothing.
1301             }
1302         }
1303
1304         // Set the pinned SSL certificate switch listener.
1305         pinnedSslCertificateSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1306             // Update the icon color.
1307             pinnedSslCertificateImageView.isSelected = isChecked
1308
1309             // Update the views.
1310             if (isChecked) {  // SSL certificate pinning is enabled.
1311                 // Update the visibility of the saved SSL certificate.
1312                 if (savedSslIssuedToCNameString == null)
1313                     savedSslCardView.visibility = View.GONE
1314                 else
1315                     savedSslCardView.visibility = View.VISIBLE
1316
1317                 // Update the visibility of the current website SSL certificate.
1318                 if (DomainsActivity.sslIssuedToCName == null) {
1319                     // Hide the SSL certificate.
1320                     currentSslCardView.visibility = View.GONE
1321
1322                     // Show the instruction.
1323                     noCurrentWebsiteCertificateTextView.visibility = View.VISIBLE
1324                 } else {
1325                     // Show the SSL certificate.
1326                     currentSslCardView.visibility = View.VISIBLE
1327
1328                     // Hide the instruction.
1329                     noCurrentWebsiteCertificateTextView.visibility = View.GONE
1330                 }
1331
1332                 // Set the status of the radio buttons.
1333                 if (savedSslCardView.visibility == View.VISIBLE) {  // The saved SSL certificate is displayed.
1334                     // Check the saved SSL certificate radio button.
1335                     savedSslCertificateRadioButton.isChecked = true
1336
1337                     // Uncheck the current website SSL certificate radio button.
1338                     currentWebsiteCertificateRadioButton.isChecked = false
1339
1340                     // Set the background of the saved SSL certificate linear layout to be transparent.
1341                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1342
1343                     // Darken the background of the current website SSL certificate linear layout according to the theme.
1344                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1345
1346                     // Scroll to the current website SSL certificate card.
1347                     savedSslCardView.parent.requestChildFocus(savedSslCardView, savedSslCardView)
1348                 } else if (currentSslCardView.visibility == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1349                     // Check the current website SSL certificate radio button.
1350                     currentWebsiteCertificateRadioButton.isChecked = true
1351
1352                     // Uncheck the saved SSL certificate radio button.
1353                     savedSslCertificateRadioButton.isChecked = false
1354
1355                     // Set the background of the current website SSL certificate linear layout to be transparent.
1356                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1357
1358                     // Darken the background of the saved SSL certificate linear layout according to the theme.
1359                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1360
1361                     // Scroll to the current website SSL certificate card.
1362                     currentSslCardView.parent.requestChildFocus(currentSslCardView, currentSslCardView)
1363                 } else {  // Neither SSL certificate is visible.
1364                     // Uncheck both radio buttons.
1365                     savedSslCertificateRadioButton.isChecked = false
1366                     currentWebsiteCertificateRadioButton.isChecked = false
1367
1368                     // Scroll to the current website SSL certificate card.
1369                     noCurrentWebsiteCertificateTextView.parent.requestChildFocus(noCurrentWebsiteCertificateTextView, noCurrentWebsiteCertificateTextView)
1370                 }
1371             } else {  // SSL certificate pinning is disabled.
1372                 // Hide the SSl certificates and instructions.
1373                 savedSslCardView.visibility = View.GONE
1374                 currentSslCardView.visibility = View.GONE
1375                 noCurrentWebsiteCertificateTextView.visibility = View.GONE
1376
1377                 // Uncheck the radio buttons.
1378                 savedSslCertificateRadioButton.isChecked = false
1379                 currentWebsiteCertificateRadioButton.isChecked = false
1380             }
1381         }
1382
1383         // Set the saved SSL card view listener.
1384         savedSslCardView.setOnClickListener {
1385             // Check the saved SSL certificate radio button.
1386             savedSslCertificateRadioButton.isChecked = true
1387
1388             // Uncheck the current website SSL certificate radio button.
1389             currentWebsiteCertificateRadioButton.isChecked = false
1390
1391             // Set the background of the saved SSL certificate linear layout to be transparent.
1392             savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1393
1394             // Darken the background of the current website SSL certificate linear layout.
1395             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1396         }
1397
1398         // Set the saved SSL certificate radio button listener.
1399         savedSslCertificateRadioButton.setOnClickListener {
1400             // Check the saved SSL certificate radio button.
1401             savedSslCertificateRadioButton.isChecked = true
1402
1403             // Uncheck the current website SSL certificate radio button.
1404             currentWebsiteCertificateRadioButton.isChecked = false
1405
1406             // Set the background of the saved SSL certificate linear layout to be transparent.
1407             savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1408
1409             // Darken the background of the current website SSL certificate linear layout.
1410             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1411         }
1412
1413         // Set the current SSL card view listener.
1414         currentSslCardView.setOnClickListener {
1415             // Check the current website SSL certificate radio button.
1416             currentWebsiteCertificateRadioButton.isChecked = true
1417
1418             // Uncheck the saved SSL certificate radio button.
1419             savedSslCertificateRadioButton.isChecked = false
1420
1421             // Set the background of the current website SSL certificate linear layout to be transparent.
1422             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1423
1424             // Darken the background of the saved SSL certificate linear layout.
1425             savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1426         }
1427
1428         // Set the current website certificate radio button listener.
1429         currentWebsiteCertificateRadioButton.setOnClickListener {
1430             // Check the current website SSL certificate radio button.
1431             currentWebsiteCertificateRadioButton.isChecked = true
1432
1433             // Uncheck the saved SSL certificate radio button.
1434             savedSslCertificateRadioButton.isChecked = false
1435
1436             // Set the background of the current website SSL certificate linear layout to be transparent.
1437             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1438
1439             // Darken the background of the saved SSL certificate linear layout.
1440             savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1441         }
1442
1443         // Set the pinned IP addresses switch listener.
1444         pinnedIpAddressesSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1445             // Update the icon color.
1446             pinnedIpAddressesImageView.isSelected = isChecked
1447
1448             // Update the views.
1449             if (isChecked) {  // IP addresses pinning is enabled.
1450                 // Update the visibility of the saved IP addresses card view.
1451                 if (savedIpAddresses == null)
1452                     savedIpAddressesCardView.visibility = View.GONE
1453                 else
1454                     savedIpAddressesCardView.visibility = View.VISIBLE
1455
1456                 // Show the current IP addresses card view.
1457                 currentIpAddressesCardView.visibility = View.VISIBLE
1458
1459                 // Set the status of the radio buttons.
1460                 if (savedIpAddressesCardView.visibility == View.VISIBLE) {  // The saved IP addresses are visible.
1461                     // Check the saved IP addresses radio button.
1462                     savedIpAddressesRadioButton.isChecked = true
1463
1464                     // Uncheck the current IP addresses radio button.
1465                     currentIpAddressesRadioButton.isChecked = false
1466
1467                     // Set the background of the saved IP addresses linear layout to be transparent.
1468                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1469
1470                     // Darken the background of the current IP addresses linear layout.
1471                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1472                 } else {  // The saved IP addresses are not visible.
1473                     // Check the current IP addresses radio button.
1474                     currentIpAddressesRadioButton.isChecked = true
1475
1476                     // Uncheck the saved IP addresses radio button.
1477                     savedIpAddressesRadioButton.isChecked = false
1478
1479                     // Set the background of the current IP addresses linear layout to be transparent.
1480                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1481
1482                     // Darken the background of the saved IP addresses linear layout.
1483                     savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1484                 }
1485
1486                 // Scroll to the bottom of the card views.
1487                 currentIpAddressesCardView.parent.requestChildFocus(currentIpAddressesCardView, currentIpAddressesCardView)
1488             } else {  // IP addresses pinning is disabled.
1489                 // Hide the IP addresses card views.
1490                 savedIpAddressesCardView.visibility = View.GONE
1491                 currentIpAddressesCardView.visibility = View.GONE
1492
1493                 // Uncheck the radio buttons.
1494                 savedIpAddressesRadioButton.isChecked = false
1495                 currentIpAddressesRadioButton.isChecked = false
1496             }
1497         }
1498
1499         // Set the saved IP addresses card view listener.
1500         savedIpAddressesCardView.setOnClickListener {
1501             // Check the saved IP addresses radio button.
1502             savedIpAddressesRadioButton.isChecked = true
1503
1504             // Uncheck the current website IP addresses radio button.
1505             currentIpAddressesRadioButton.isChecked = false
1506
1507             // Set the background of the saved IP addresses linear layout to be transparent.
1508             savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1509
1510             // Darken the background of the current IP addresses linear layout.
1511                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1512         }
1513
1514         // Set the saved IP addresses radio button listener.
1515         savedIpAddressesRadioButton.setOnClickListener {
1516             // Check the saved IP addresses radio button.
1517             savedIpAddressesRadioButton.isChecked = true
1518
1519             // Uncheck the current website IP addresses radio button.
1520             currentIpAddressesRadioButton.isChecked = false
1521
1522             // Set the background of the saved IP addresses linear layout to be transparent.
1523             savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1524
1525             // Darken the background of the current IP addresses linear layout.
1526             currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1527         }
1528
1529         // Set the current IP addresses card view listener.
1530         currentIpAddressesCardView.setOnClickListener {
1531             // Check the current IP addresses radio button.
1532             currentIpAddressesRadioButton.isChecked = true
1533
1534             // Uncheck the saved IP addresses radio button.
1535             savedIpAddressesRadioButton.isChecked = false
1536
1537             // Set the background of the current IP addresses linear layout to be transparent.
1538             currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1539
1540             // Darken the background of the saved IP addresses linear layout.
1541             savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1542         }
1543
1544         // Set the current IP addresses radio button listener.
1545         currentIpAddressesRadioButton.setOnClickListener {
1546             // Check the current IP addresses radio button.
1547             currentIpAddressesRadioButton.isChecked = true
1548
1549             // Uncheck the saved IP addresses radio button.
1550             savedIpAddressesRadioButton.isChecked = false
1551
1552             // Set the background of the current IP addresses linear layout to be transparent.
1553             currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1554
1555             // Darken the background of the saved IP addresses linear layout.
1556             savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1557         }
1558
1559         // Set the scroll Y.
1560         domainSettingsScrollView.post { domainSettingsScrollView.scrollY = scrollY }
1561
1562         // Return the domain settings view.
1563         return domainSettingsView
1564     }
1565
1566     private fun checkDomainNameAgainstCertificate(domainName: String?, certificateCommonName: String?): Boolean {
1567         // Initialize the domain names match tracker.
1568         var domainNamesMatch = false
1569
1570         // Check various wildcard permutations if the domain name and the certificate Common Name are not empty.
1571         if ((domainName != null) && (certificateCommonName != null)) {
1572             // Check if the domains match.
1573             if (domainName == certificateCommonName)
1574                 domainNamesMatch = true
1575
1576             // If the domain name starts with a wildcard, check the base domain against all the subdomains of the certificate Common Name.
1577             if (!domainNamesMatch && domainName.startsWith("*.") && domainName.length > 2) {
1578                 // Remove the initial `*.`.
1579                 val baseDomainName = domainName.substring(2)
1580
1581                 // Create a copy of the certificate Common Name to test subdomains.
1582                 var certificateCommonNameSubdomain: String = certificateCommonName
1583
1584                 // Check all the subdomains in the certificate Common Name subdomain against the base domain name.
1585                 while (!domainNamesMatch && certificateCommonNameSubdomain.contains(".")) {  // Stop checking if the domain names match or if there are no more dots.
1586                     // Test the certificate Common Name subdomain against the base domain name.
1587                     if (certificateCommonNameSubdomain == baseDomainName)
1588                         domainNamesMatch = true
1589
1590                     // Strip out the lowest subdomain of the certificate Common Name subdomain.
1591                     certificateCommonNameSubdomain = try {
1592                         certificateCommonNameSubdomain.substring(certificateCommonNameSubdomain.indexOf(".") + 1)
1593                     } catch (e: IndexOutOfBoundsException) {  // The certificate Common Name subdomain ends with a dot.
1594                         ""
1595                     }
1596                 }
1597             }
1598
1599             // If the certificate Common Name starts with a wildcard, check the base common name against all the subdomains of the domain name.
1600             if (!domainNamesMatch && certificateCommonName.startsWith("*.") && certificateCommonName.length > 2) {
1601                 // Remove the initial `*.`.
1602                 val baseCertificateCommonName = certificateCommonName.substring(2)
1603
1604                 // Setup a copy of domain name to test subdomains.
1605                 var domainNameSubdomain: String = domainName
1606
1607                 // Check all the subdomains in the domain name subdomain against the base certificate Common Name.
1608                 while (!domainNamesMatch && domainNameSubdomain.contains(".") && domainNameSubdomain.length > 2) {
1609                     // Test the domain name subdomain  against the base certificate Common Name.
1610                     if (domainNameSubdomain == baseCertificateCommonName)
1611                         domainNamesMatch = true
1612
1613                     // Strip out the lowest subdomain of the domain name subdomain.
1614                     domainNameSubdomain = try {
1615                         domainNameSubdomain.substring(domainNameSubdomain.indexOf(".") + 1)
1616                     } catch (e: IndexOutOfBoundsException) {  // `domainNameSubdomain` ends with a dot.
1617                         ""
1618                     }
1619                 }
1620             }
1621
1622             // If both names start with a wildcard, check if the root of one contains the root of the other.
1623             if (!domainNamesMatch && domainName.startsWith("*.") && domainName.length > 2 && certificateCommonName.startsWith("*.") && certificateCommonName.length > 2) {
1624                 // Remove the wildcards.
1625                 val rootDomainName = domainName.substring(2)
1626                 val rootCertificateCommonName = certificateCommonName.substring(2)
1627
1628                 // Check if one name ends with the contents of the other.  If so, there will be overlap in the their wildcard subdomains.
1629                 if (rootDomainName.endsWith(rootCertificateCommonName) || rootCertificateCommonName.endsWith(rootDomainName))
1630                     domainNamesMatch = true
1631             }
1632         }
1633
1634         return domainNamesMatch
1635     }
1636 }