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