2 * Copyright 2017-2022 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
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.
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.
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/>.
20 package com.stoutner.privacybrowser.fragments
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
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
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
57 import java.lang.IndexOutOfBoundsException
58 import java.text.DateFormat
59 import java.util.Calendar
62 class DomainSettingsFragment : Fragment() {
63 // Define the class variables.
64 private var scrollY = 0
67 // Define the public constants.
68 const val DATABASE_ID = "database_id"
69 const val SCROLL_Y = "scroll_y"
71 // Define the public variables. `databaseId` is public so it can be accessed from `DomainsActivity`.
75 override fun onCreate(savedInstanceState: Bundle?) {
76 // Run the default commands.
77 super.onCreate(savedInstanceState)
79 // Store the arguments in class variables.
80 databaseId = requireArguments().getInt(DATABASE_ID)
81 scrollY = requireArguments().getInt(SCROLL_Y)
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)
88 // Get the current theme status.
89 val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
91 // Get a handle for the shared preference.
92 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
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)
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)
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) + " "
189 // Initialize the database handler.
190 val domainsDatabaseHelper = DomainsDatabaseHelper(requireContext())
192 // Get the database cursor for this ID.
193 val domainCursor = domainsDatabaseHelper.getCursorForId(databaseId)
195 // Move to the first row.
196 domainCursor.moveToFirst()
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))
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))
231 // Initialize the saved SSL certificate date variables.
232 var savedSslStartDate: Date? = null
233 var savedSslEndDate: Date? = null
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)
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)
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)
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
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)
273 // Create the date spannable string builders.
274 val savedSslStartDateStringBuilder: SpannableStringBuilder = if (savedSslStartDate == null)
275 SpannableStringBuilder(startDateLabel)
277 SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslStartDate))
279 val savedSslEndDateStringBuilder: SpannableStringBuilder = if (savedSslEndDate == null)
280 SpannableStringBuilder(endDateLabel)
282 SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslEndDate))
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))
288 // Set the domain name from the the database cursor.
289 domainNameEditText.setText(domainNameString)
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) {
297 override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
301 override fun afterTextChanged(s: Editable) {
302 // Get the new domain name.
303 val newDomainName = domainNameEditText.text.toString()
305 // Check the saved SSL certificate against the new domain name.
306 val savedSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, savedSslIssuedToCNameString)
308 // Create a spannable string builder for the saved certificate's Common Name.
309 val savedSslCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString)
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)
315 savedSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, savedSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
318 // Update the saved SSL issued to CName text view.
319 savedSslIssuedToCNameTextView.text = savedSslCNameStringBuilder
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)
326 // Create a spannable string builder for the current website certificate's Common Name.
327 val currentSslCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName)
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)
333 currentSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, currentSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
336 // Update the current SSL issued to CName text view.
337 currentSslIssuedToCNameTextView.text = currentSslCNameStringBuilder
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
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
371 // Set the JavaScript icon.
372 if (javaScriptInt == 1)
373 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null))
375 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null))
377 // Set the DOM storage switch status based on the JavaScript status.
378 domStorageSwitch.isEnabled = (javaScriptInt == 1)
380 // Set the DOM storage icon ghosted status based on the JavaScript status.
381 domStorageImageView.isEnabled = (javaScriptInt == 1)
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
390 // Set Fanboy's Social Blocking List switch status based on the Annoyance List status.
391 fanboysSocialBlockingListSwitch.isEnabled = (fanboysAnnoyanceListInt == 0)
393 // Set the Social Blocking List icon ghosted status based on the Annoyance List status.
394 fanboysSocialBlockingListImageView.isEnabled = (fanboysAnnoyanceListInt == 0)
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
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)
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)
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)
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
419 // Display the WebView default user agent.
420 MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentTextView.text = webViewDefaultUserAgentString
422 // Display the custom user agent.
423 MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT -> userAgentTextView.text = defaultCustomUserAgentString
425 // Get the user agent string from the user agent data array.
426 else -> userAgentTextView.text = userAgentDataArray[defaultUserAgentArrayPosition]
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)
433 // Hide the user agent text view.
434 userAgentTextView.visibility = View.GONE
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)
443 // Show the user agent text view.
444 userAgentTextView.visibility = View.VISIBLE
446 // Hide the custom user agent edit text.
447 customUserAgentEditText.visibility = View.GONE
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]
459 // Open the user agent spinner when the text view is clicked.
460 userAgentTextView.setOnClickListener { userAgentSpinner.performClick() }
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)
467 // Show the default font size text view.
468 defaultFontSizeTextView.visibility = View.VISIBLE
470 // Hide the custom font size edit text.
471 customFontSizeEditText.visibility = View.GONE
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)
479 // Hide the default font size text view.
480 defaultFontSizeTextView.visibility = View.GONE
482 // Show the custom font size edit text.
483 customFontSizeEditText.visibility = View.GONE
485 // Set the custom font size.
486 customFontSizeEditText.setText(fontSizeInt.toString())
489 // Initialize the default font size percentage string.
490 val defaultFontSizePercentageString = "$defaultFontSizeString%"
492 // Set the default font size text in the text view.
493 defaultFontSizeTextView.text = defaultFontSizePercentageString
495 // Open the font size spinner when the text view is clicked.
496 defaultFontSizeTextView.setOnClickListener { fontSizeSpinner.performClick() }
498 // Select the swipe-to-refresh selection in the spinner.
499 swipeToRefreshSpinner.setSelection(swipeToRefreshInt)
501 // Set the swipe-to-refresh text.
502 if (defaultSwipeToRefresh)
503 swipeToRefreshTextView.text = swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
505 swipeToRefreshTextView.text = swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
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
513 // Show the swipe-to-refresh text view.
514 swipeToRefreshTextView.visibility = View.VISIBLE
517 DomainsDatabaseHelper.ENABLED -> {
518 // Set the icon color.
519 swipeToRefreshImageView.isSelected = true
521 // Hide the swipe-to-refresh text view.
522 swipeToRefreshTextView.visibility = View.GONE
525 DomainsDatabaseHelper.DISABLED -> {
526 // Set the icon color.
527 swipeToRefreshImageView.isSelected = false
529 // Hide the swipe-to-refresh text view.
530 swipeToRefreshTextView.visibility = View.GONE
534 // Open the swipe-to-refresh spinner when the text view is clicked.
535 swipeToRefreshTextView.setOnClickListener { swipeToRefreshSpinner.performClick() }
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)
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.
548 // Select the WebView theme in the spinner.
549 webViewThemeSpinner.setSelection(webViewThemeInt)
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]
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]
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
573 // Show the WebView theme text view.
574 webViewThemeTextView.visibility = View.VISIBLE
577 DomainsDatabaseHelper.LIGHT_THEME -> {
578 // Set the icon color.
579 webViewThemeImageView.isSelected = true
581 // Hide the WebView theme text view.
582 webViewThemeTextView.visibility = View.GONE
585 DomainsDatabaseHelper.DARK_THEME -> {
586 // Set the icon color.
587 webViewThemeImageView.isSelected = false
589 // Hide the WebView theme text view.
590 webViewThemeTextView.visibility = View.GONE
594 // Open the WebView theme spinner when the text view is clicked.
595 webViewThemeTextView.setOnClickListener { webViewThemeSpinner.performClick() }
597 // Select the wide viewport in the spinner.
598 wideViewportSpinner.setSelection(wideViewportInt)
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)
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
611 // Show the wide viewport text view.
612 wideViewportTextView.visibility = View.VISIBLE
615 DomainsDatabaseHelper.ENABLED -> {
616 // Set the icon color.
617 wideViewportImageView.isSelected = true
619 // Hide the wide viewport text view.
620 wideViewportTextView.visibility = View.GONE
623 DomainsDatabaseHelper.DISABLED -> {
624 // Set the icon color.
625 wideViewportImageView.isSelected = false
627 // Hide the wide viewport text view.
628 wideViewportTextView.visibility = View.GONE
632 // Open the wide viewport spinner when the text view is clicked.
633 wideViewportTextView.setOnClickListener { wideViewportSpinner.performClick() }
635 // Display the website images mode in the spinner.
636 displayWebpageImagesSpinner.setSelection(displayImagesInt)
638 // Set the default display images text.
639 if (defaultDisplayWebpageImages)
640 displayImagesTextView.text = displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
642 displayImagesTextView.text = displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
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
650 // Show the display images text view.
651 displayImagesTextView.visibility = View.VISIBLE
654 DomainsDatabaseHelper.ENABLED -> {
655 // Set the icon color.
656 displayWebpageImagesImageView.isSelected = true
658 // Hide the display images text view.
659 displayImagesTextView.visibility = View.GONE
662 DomainsDatabaseHelper.DISABLED -> {
663 // Set the icon color.
664 displayWebpageImagesImageView.isSelected = false
666 // Hide the display images text view.
667 displayImagesTextView.visibility = View.GONE
671 // Open the display images spinner when the text view is clicked.
672 displayImagesTextView.setOnClickListener { displayWebpageImagesSpinner.performClick() }
674 // Store the current date.
675 val currentDate = Calendar.getInstance().time
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)
684 // Check the certificate Common Name against the domain name.
685 val savedSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslIssuedToCNameString)
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)
691 savedSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, savedSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
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)
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)
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
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)
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))
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)
738 // Check the certificate Common Name against the domain name.
739 val currentSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, DomainsActivity.sslIssuedToCName)
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)
745 currentSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, currentSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
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)
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)
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
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
776 savedSslCardView.visibility = View.VISIBLE
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
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
789 // Hide the instruction.
790 noCurrentWebsiteCertificateTextView.visibility = View.GONE
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
798 // Uncheck the current website SSL certificate radio button.
799 currentWebsiteCertificateRadioButton.isChecked = false
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
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
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
820 // Uncheck the radio buttons.
821 savedSslCertificateRadioButton.isChecked = false
822 currentWebsiteCertificateRadioButton.isChecked = false
825 // Populate the saved and current IP addresses.
826 savedIpAddressesTextView.text = savedIpAddresses
827 currentIpAddressesTextView.text = DomainsActivity.currentIpAddresses
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
837 // Set the visibility of the current IP addresses.
838 currentIpAddressesCardView.visibility = View.VISIBLE
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
845 // Uncheck the current IP addresses radio button.
846 currentIpAddressesRadioButton.isChecked = false
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
854 // Uncheck the saved IP addresses radio button.
855 savedIpAddressesRadioButton.isChecked = false
857 } else { // IP addresses are not pinned.
858 // Hide the IP addresses card views.
859 savedIpAddressesCardView.visibility = View.GONE
860 currentIpAddressesCardView.visibility = View.GONE
862 // Uncheck the radio buttons.
863 savedIpAddressesRadioButton.isChecked = false
864 currentIpAddressesRadioButton.isChecked = false
868 // Set the JavaScript switch listener.
869 javaScriptSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
870 // Update the JavaScript icon.
872 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null))
874 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null))
876 // Set the DOM storage switch status.
877 domStorageSwitch.isEnabled = isChecked
879 // Set the DOM storage ghosted icon status.
880 domStorageImageView.isEnabled = isChecked
883 // Set the cookies switch listener.
884 cookiesSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
885 // Update the icon color.
886 cookiesImageView.isSelected = isChecked
889 // Set the DOM Storage switch listener.
890 domStorageSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
891 // Update the icon color.
892 domStorageImageView.isSelected = isChecked
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
903 // Set the EasyList switch listener.
904 easyListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
905 // Update the icon color.
906 easyListImageView.isSelected = isChecked
909 // Set the EasyPrivacy switch listener.
910 easyPrivacySwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
911 // Update the icon color.
912 easyPrivacyImageView.isSelected = isChecked
915 // Set the Fanboy's Annoyance List switch listener.
916 fanboysAnnoyanceListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
917 // Update the icon color.
918 fanboysAnnoyanceListImageView.isSelected = isChecked
920 // Set Fanboy's Social Blocking List switch position.
921 fanboysSocialBlockingListSwitch.isEnabled = !isChecked
923 // Set the Social Blocking List icon ghosted status.
924 fanboysSocialBlockingListImageView.isEnabled = !isChecked
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
933 // Set the UltraList switch listener.
934 ultraListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
935 // Update the icon color.
936 ultraListImageView.isSelected = isChecked
939 // Set the UltraPrivacy switch listener.
940 ultraPrivacySwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
941 // Update the icon color.
942 ultraPrivacyImageView.isSelected = isChecked
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
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.
956 MainWebViewActivity.DOMAINS_SYSTEM_DEFAULT_USER_AGENT -> {
957 // Show the user agent text view.
958 userAgentTextView.visibility = View.VISIBLE
960 // Hide the custom user agent edit text.
961 customUserAgentEditText.visibility = View.GONE
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
968 // Display the `WebView` default user agent.
969 MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentTextView.text = webViewDefaultUserAgentString
971 // Display the custom user agent.
972 MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT -> userAgentTextView.text = defaultCustomUserAgentString
974 // Get the user agent string from the user agent data array.
975 else -> userAgentTextView.text = userAgentDataArray[defaultUserAgentArrayPosition]
979 MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT -> {
980 // Show the user agent text view.
981 userAgentTextView.visibility = View.VISIBLE
983 // Set the user agent text.
984 userAgentTextView.text = webViewDefaultUserAgentString
986 // Hide the custom user agent EditTex.
987 customUserAgentEditText.visibility = View.GONE
990 MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT -> {
991 // Hide the user agent TextView.
992 userAgentTextView.visibility = View.GONE
994 // Show the custom user agent edit text.
995 customUserAgentEditText.visibility = View.VISIBLE
997 // Set the current user agent name as the text.
998 customUserAgentEditText.setText(currentUserAgentName)
1002 // Show the user agent text view.
1003 userAgentTextView.visibility = View.VISIBLE
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]
1008 // Hide the custom user agent edit text.
1009 customUserAgentEditText.visibility = View.GONE
1014 override fun onNothingSelected(parent: AdapterView<*>?) {
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
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
1033 // Show the custom font size edit text.
1034 customFontSizeEditText.visibility = View.VISIBLE
1038 override fun onNothingSelected(parent: AdapterView<*>?) {
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.
1048 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1049 // Set the icon color.
1050 swipeToRefreshImageView.isSelected = defaultSwipeToRefresh
1052 // Show the swipe-to-refresh text view.
1053 swipeToRefreshTextView.visibility = View.VISIBLE
1056 DomainsDatabaseHelper.ENABLED -> {
1057 // Set the icon color.
1058 swipeToRefreshImageView.isSelected = true
1060 // Hide the swipe-to-refresh text view.
1061 swipeToRefreshTextView.visibility = View.GONE
1064 DomainsDatabaseHelper.DISABLED -> {
1065 // Set the icon color.
1066 swipeToRefreshImageView.isSelected = false
1068 // Hide the swipe-to-refresh text view.
1069 swipeToRefreshTextView.visibility = View.GONE
1074 override fun onNothingSelected(parent: AdapterView<*>?) {
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.
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
1092 // Show the WebView theme text view.
1093 webViewThemeTextView.visibility = View.VISIBLE
1096 DomainsDatabaseHelper.LIGHT_THEME -> {
1097 // Set the icon color.
1098 webViewThemeImageView.isSelected = true
1100 // Hide the WebView theme text view.
1101 webViewThemeTextView.visibility = View.GONE
1104 DomainsDatabaseHelper.DARK_THEME -> {
1105 // Set the icon color.
1106 webViewThemeImageView.isSelected = false
1108 // Hide the WebView theme text view.
1109 webViewThemeTextView.visibility = View.GONE
1114 override fun onNothingSelected(parent: AdapterView<*>?) {
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.
1124 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1125 // Set the icon color.
1126 wideViewportImageView.isSelected = defaultWideViewport
1128 // Show the wide viewport text view.
1129 wideViewportTextView.visibility = View.VISIBLE
1132 DomainsDatabaseHelper.ENABLED -> {
1133 // Set the icon color.
1134 wideViewportImageView.isSelected = true
1136 // Hide the wide viewport text view.
1137 wideViewportTextView.visibility = View.GONE
1140 DomainsDatabaseHelper.DISABLED -> {
1141 // Set the icon color.
1142 wideViewportImageView.isSelected = false
1144 // Hid ethe wide viewport text view.
1145 wideViewportTextView.visibility = View.GONE
1150 override fun onNothingSelected(parent: AdapterView<*>?) {
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.
1160 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1161 // Set the icon color.
1162 displayWebpageImagesImageView.isSelected = defaultDisplayWebpageImages
1164 // Show the display images text view.
1165 displayImagesTextView.visibility = View.VISIBLE
1168 DomainsDatabaseHelper.ENABLED -> {
1169 // Set the icon color.
1170 displayWebpageImagesImageView.isSelected = true
1172 // Hide the display images text view.
1173 displayImagesTextView.visibility = View.GONE
1176 DomainsDatabaseHelper.DISABLED -> {
1177 // Set the icon color.
1178 displayWebpageImagesImageView.isSelected = false
1180 // Hide the display images text view.
1181 displayImagesTextView.visibility = View.GONE
1186 override fun onNothingSelected(parent: AdapterView<*>?) {
1191 // Set the pinned SSL certificate switch listener.
1192 pinnedSslCertificateSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1193 // Update the icon color.
1194 pinnedSslCertificateImageView.isSelected = isChecked
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
1202 savedSslCardView.visibility = View.VISIBLE
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
1209 // Show the instruction.
1210 noCurrentWebsiteCertificateTextView.visibility = View.VISIBLE
1212 // Show the SSL certificate.
1213 currentSslCardView.visibility = View.VISIBLE
1215 // Hide the instruction.
1216 noCurrentWebsiteCertificateTextView.visibility = View.GONE
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
1224 // Uncheck the current website SSL certificate radio button.
1225 currentWebsiteCertificateRadioButton.isChecked = false
1227 // Set the background of the saved SSL certificate linear layout to be transparent.
1228 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1230 // Darken the background of the current website SSL certificate linear layout according to the theme.
1231 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
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
1239 // Uncheck the saved SSL certificate radio button.
1240 savedSslCertificateRadioButton.isChecked = false
1242 // Set the background of the current website SSL certificate linear layout to be transparent.
1243 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1245 // Darken the background of the saved SSL certificate linear layout according to the theme.
1246 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
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
1255 // Scroll to the current website SSL certificate card.
1256 noCurrentWebsiteCertificateTextView.parent.requestChildFocus(noCurrentWebsiteCertificateTextView, noCurrentWebsiteCertificateTextView)
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
1264 // Uncheck the radio buttons.
1265 savedSslCertificateRadioButton.isChecked = false
1266 currentWebsiteCertificateRadioButton.isChecked = false
1270 // Set the saved SSL card view listener.
1271 savedSslCardView.setOnClickListener {
1272 // Check the saved SSL certificate radio button.
1273 savedSslCertificateRadioButton.isChecked = true
1275 // Uncheck the current website SSL certificate radio button.
1276 currentWebsiteCertificateRadioButton.isChecked = false
1278 // Set the background of the saved SSL certificate linear layout to be transparent.
1279 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1281 // Darken the background of the current website SSL certificate linear layout.
1282 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1285 // Set the saved SSL certificate radio button listener.
1286 savedSslCertificateRadioButton.setOnClickListener {
1287 // Check the saved SSL certificate radio button.
1288 savedSslCertificateRadioButton.isChecked = true
1290 // Uncheck the current website SSL certificate radio button.
1291 currentWebsiteCertificateRadioButton.isChecked = false
1293 // Set the background of the saved SSL certificate linear layout to be transparent.
1294 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1296 // Darken the background of the current website SSL certificate linear layout.
1297 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1300 // Set the current SSL card view listener.
1301 currentSslCardView.setOnClickListener {
1302 // Check the current website SSL certificate radio button.
1303 currentWebsiteCertificateRadioButton.isChecked = true
1305 // Uncheck the saved SSL certificate radio button.
1306 savedSslCertificateRadioButton.isChecked = false
1308 // Set the background of the current website SSL certificate linear layout to be transparent.
1309 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1311 // Darken the background of the saved SSL certificate linear layout.
1312 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
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
1320 // Uncheck the saved SSL certificate radio button.
1321 savedSslCertificateRadioButton.isChecked = false
1323 // Set the background of the current website SSL certificate linear layout to be transparent.
1324 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1326 // Darken the background of the saved SSL certificate linear layout.
1327 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1330 // Set the pinned IP addresses switch listener.
1331 pinnedIpAddressesSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1332 // Update the icon color.
1333 pinnedIpAddressesImageView.isSelected = isChecked
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
1341 savedIpAddressesCardView.visibility = View.VISIBLE
1343 // Show the current IP addresses card view.
1344 currentIpAddressesCardView.visibility = View.VISIBLE
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
1351 // Uncheck the current IP addresses radio button.
1352 currentIpAddressesRadioButton.isChecked = false
1354 // Set the background of the saved IP addresses linear layout to be transparent.
1355 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
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
1363 // Uncheck the saved IP addresses radio button.
1364 savedIpAddressesRadioButton.isChecked = false
1366 // Set the background of the current IP addresses linear layout to be transparent.
1367 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1369 // Darken the background of the saved IP addresses linear layout.
1370 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
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
1380 // Uncheck the radio buttons.
1381 savedIpAddressesRadioButton.isChecked = false
1382 currentIpAddressesRadioButton.isChecked = false
1386 // Set the saved IP addresses card view listener.
1387 savedIpAddressesCardView.setOnClickListener {
1388 // Check the saved IP addresses radio button.
1389 savedIpAddressesRadioButton.isChecked = true
1391 // Uncheck the current website IP addresses radio button.
1392 currentIpAddressesRadioButton.isChecked = false
1394 // Set the background of the saved IP addresses linear layout to be transparent.
1395 savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1397 // Darken the background of the current IP addresses linear layout.
1398 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1401 // Set the saved IP addresses radio button listener.
1402 savedIpAddressesRadioButton.setOnClickListener {
1403 // Check the saved IP addresses radio button.
1404 savedIpAddressesRadioButton.isChecked = true
1406 // Uncheck the current website IP addresses radio button.
1407 currentIpAddressesRadioButton.isChecked = false
1409 // Set the background of the saved IP addresses linear layout to be transparent.
1410 savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1412 // Darken the background of the current IP addresses linear layout.
1413 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1416 // Set the current IP addresses card view listener.
1417 currentIpAddressesCardView.setOnClickListener {
1418 // Check the current IP addresses radio button.
1419 currentIpAddressesRadioButton.isChecked = true
1421 // Uncheck the saved IP addresses radio button.
1422 savedIpAddressesRadioButton.isChecked = false
1424 // Set the background of the current IP addresses linear layout to be transparent.
1425 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1427 // Darken the background of the saved IP addresses linear layout.
1428 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1431 // Set the current IP addresses radio button listener.
1432 currentIpAddressesRadioButton.setOnClickListener {
1433 // Check the current IP addresses radio button.
1434 currentIpAddressesRadioButton.isChecked = true
1436 // Uncheck the saved IP addresses radio button.
1437 savedIpAddressesRadioButton.isChecked = false
1439 // Set the background of the current IP addresses linear layout to be transparent.
1440 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1442 // Darken the background of the saved IP addresses linear layout.
1443 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1446 // Set the scroll Y.
1447 domainSettingsScrollView.post { domainSettingsScrollView.scrollY = scrollY }
1449 // Return the domain settings view.
1450 return domainSettingsView
1453 private fun checkDomainNameAgainstCertificate(domainName: String?, certificateCommonName: String?): Boolean {
1454 // Initialize the domain names match tracker.
1455 var domainNamesMatch = false
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
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)
1468 // Create a copy of the certificate Common Name to test subdomains.
1469 var certificateCommonNameSubdomain: String = certificateCommonName
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
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.
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)
1491 // Setup a copy of domain name to test subdomains.
1492 var domainNameSubdomain: String = domainName
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
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.
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)
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
1521 return domainNamesMatch