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