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