2 * Copyright 2017-2023 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.DOMAINS_CUSTOM_USER_AGENT
54 import com.stoutner.privacybrowser.activities.DOMAINS_SYSTEM_DEFAULT_USER_AGENT
55 import com.stoutner.privacybrowser.activities.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT
56 import com.stoutner.privacybrowser.activities.SETTINGS_CUSTOM_USER_AGENT
57 import com.stoutner.privacybrowser.activities.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT
58 import com.stoutner.privacybrowser.activities.UNRECOGNIZED_USER_AGENT
59 import com.stoutner.privacybrowser.activities.DomainsActivity
60 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper
62 import java.lang.IndexOutOfBoundsException
63 import java.text.DateFormat
64 import java.util.Calendar
67 class DomainSettingsFragment : Fragment() {
68 // Define the class variables.
69 private var scrollY = 0
72 // Define the public constants.
73 const val DATABASE_ID = "database_id"
74 const val SCROLL_Y = "scroll_y"
76 // Define the public variables. `databaseId` is public so it can be accessed from `DomainsActivity`.
80 override fun onCreate(savedInstanceState: Bundle?) {
81 // Run the default commands.
82 super.onCreate(savedInstanceState)
84 // Store the arguments in class variables.
85 databaseId = requireArguments().getInt(DATABASE_ID)
86 scrollY = requireArguments().getInt(SCROLL_Y)
89 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
90 // Inflate the layout. The fragment will take care of attaching the root automatically.
91 val domainSettingsView = inflater.inflate(R.layout.domain_settings_fragment, container, false)
93 // Get the current theme status.
94 val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
96 // Get a handle for the shared preference.
97 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
99 // Store the default settings.
100 val defaultUserAgentName = sharedPreferences.getString(getString(R.string.user_agent_key), getString(R.string.user_agent_default_value))
101 val defaultCustomUserAgentString = sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value))
102 val defaultFontSizeString = sharedPreferences.getString(getString(R.string.font_size_key), getString(R.string.font_size_default_value))
103 val defaultSwipeToRefresh = sharedPreferences.getBoolean(getString(R.string.swipe_to_refresh_key), true)
104 val defaultWebViewTheme = sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value))
105 val defaultWideViewport = sharedPreferences.getBoolean(getString(R.string.wide_viewport_key), true)
106 val defaultDisplayWebpageImages = sharedPreferences.getBoolean(getString(R.string.display_webpage_images_key), true)
108 // Get handles for the views.
109 val domainSettingsScrollView = domainSettingsView.findViewById<ScrollView>(R.id.domain_settings_scrollview)
110 val domainNameEditText = domainSettingsView.findViewById<EditText>(R.id.domain_settings_name_edittext)
111 val javaScriptImageView = domainSettingsView.findViewById<ImageView>(R.id.javascript_imageview)
112 val javaScriptSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.javascript_switch)
113 val cookiesImageView = domainSettingsView.findViewById<ImageView>(R.id.cookies_imageview)
114 val cookiesSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.cookies_switch)
115 val domStorageImageView = domainSettingsView.findViewById<ImageView>(R.id.dom_storage_imageview)
116 val domStorageSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.dom_storage_switch)
117 val formDataImageView = domainSettingsView.findViewById<ImageView>(R.id.form_data_imageview) // The form data views can be remove once the minimum API >= 26.
118 val formDataSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.form_data_switch) // The form data views can be remove once the minimum API >= 26.
119 val easyListImageView = domainSettingsView.findViewById<ImageView>(R.id.easylist_imageview)
120 val easyListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.easylist_switch)
121 val easyPrivacyImageView = domainSettingsView.findViewById<ImageView>(R.id.easyprivacy_imageview)
122 val easyPrivacySwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.easyprivacy_switch)
123 val fanboysAnnoyanceListImageView = domainSettingsView.findViewById<ImageView>(R.id.fanboys_annoyance_list_imageview)
124 val fanboysAnnoyanceListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.fanboys_annoyance_list_switch)
125 val fanboysSocialBlockingListImageView = domainSettingsView.findViewById<ImageView>(R.id.fanboys_social_blocking_list_imageview)
126 val fanboysSocialBlockingListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.fanboys_social_blocking_list_switch)
127 val ultraListImageView = domainSettingsView.findViewById<ImageView>(R.id.ultralist_imageview)
128 val ultraListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.ultralist_switch)
129 val ultraPrivacyImageView = domainSettingsView.findViewById<ImageView>(R.id.ultraprivacy_imageview)
130 val ultraPrivacySwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.ultraprivacy_switch)
131 val blockAllThirdPartyRequestsImageView = domainSettingsView.findViewById<ImageView>(R.id.block_all_third_party_requests_imageview)
132 val blockAllThirdPartyRequestsSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.block_all_third_party_requests_switch)
133 val userAgentSpinner = domainSettingsView.findViewById<Spinner>(R.id.user_agent_spinner)
134 val userAgentTextView = domainSettingsView.findViewById<TextView>(R.id.user_agent_textview)
135 val customUserAgentEditText = domainSettingsView.findViewById<EditText>(R.id.custom_user_agent_edittext)
136 val fontSizeSpinner = domainSettingsView.findViewById<Spinner>(R.id.font_size_spinner)
137 val defaultFontSizeTextView = domainSettingsView.findViewById<TextView>(R.id.default_font_size_textview)
138 val customFontSizeEditText = domainSettingsView.findViewById<EditText>(R.id.custom_font_size_edittext)
139 val swipeToRefreshImageView = domainSettingsView.findViewById<ImageView>(R.id.swipe_to_refresh_imageview)
140 val swipeToRefreshSpinner = domainSettingsView.findViewById<Spinner>(R.id.swipe_to_refresh_spinner)
141 val swipeToRefreshTextView = domainSettingsView.findViewById<TextView>(R.id.swipe_to_refresh_textview)
142 val webViewThemeLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.webview_theme_linearlayout)
143 val webViewThemeImageView = domainSettingsView.findViewById<ImageView>(R.id.webview_theme_imageview)
144 val webViewThemeSpinner = domainSettingsView.findViewById<Spinner>(R.id.webview_theme_spinner)
145 val webViewThemeTextView = domainSettingsView.findViewById<TextView>(R.id.webview_theme_textview)
146 val wideViewportImageView = domainSettingsView.findViewById<ImageView>(R.id.wide_viewport_imageview)
147 val wideViewportSpinner = domainSettingsView.findViewById<Spinner>(R.id.wide_viewport_spinner)
148 val wideViewportTextView = domainSettingsView.findViewById<TextView>(R.id.wide_viewport_textview)
149 val displayWebpageImagesImageView = domainSettingsView.findViewById<ImageView>(R.id.display_webpage_images_imageview)
150 val displayWebpageImagesSpinner = domainSettingsView.findViewById<Spinner>(R.id.display_webpage_images_spinner)
151 val displayImagesTextView = domainSettingsView.findViewById<TextView>(R.id.display_webpage_images_textview)
152 val pinnedSslCertificateImageView = domainSettingsView.findViewById<ImageView>(R.id.pinned_ssl_certificate_imageview)
153 val pinnedSslCertificateSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.pinned_ssl_certificate_switch)
154 val savedSslCardView = domainSettingsView.findViewById<CardView>(R.id.saved_ssl_certificate_cardview)
155 val savedSslCertificateLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.saved_ssl_certificate_linearlayout)
156 val savedSslCertificateRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.saved_ssl_certificate_radiobutton)
157 val savedSslIssuedToCNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_cname)
158 val savedSslIssuedToONameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_oname)
159 val savedSslIssuedToUNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_uname)
160 val savedSslIssuedByCNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_cname)
161 val savedSslIssuedByONameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_oname)
162 val savedSslIssuedByUNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_uname)
163 val savedSslStartDateTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_start_date)
164 val savedSslEndDateTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_end_date)
165 val currentSslCardView = domainSettingsView.findViewById<CardView>(R.id.current_website_certificate_cardview)
166 val currentWebsiteCertificateLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.current_website_certificate_linearlayout)
167 val currentWebsiteCertificateRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.current_website_certificate_radiobutton)
168 val currentSslIssuedToCNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_cname)
169 val currentSslIssuedToONameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_oname)
170 val currentSslIssuedToUNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_uname)
171 val currentSslIssuedByCNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_cname)
172 val currentSslIssuedByONameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_oname)
173 val currentSslIssuedByUNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_uname)
174 val currentSslStartDateTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_start_date)
175 val currentSslEndDateTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_end_date)
176 val noCurrentWebsiteCertificateTextView = domainSettingsView.findViewById<TextView>(R.id.no_current_website_certificate)
177 val pinnedIpAddressesImageView = domainSettingsView.findViewById<ImageView>(R.id.pinned_ip_addresses_imageview)
178 val pinnedIpAddressesSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.pinned_ip_addresses_switch)
179 val savedIpAddressesCardView = domainSettingsView.findViewById<CardView>(R.id.saved_ip_addresses_cardview)
180 val savedIpAddressesLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.saved_ip_addresses_linearlayout)
181 val savedIpAddressesRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.saved_ip_addresses_radiobutton)
182 val savedIpAddressesTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ip_addresses_textview)
183 val currentIpAddressesCardView = domainSettingsView.findViewById<CardView>(R.id.current_ip_addresses_cardview)
184 val currentIpAddressesLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.current_ip_addresses_linearlayout)
185 val currentIpAddressesRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.current_ip_addresses_radiobutton)
186 val currentIpAddressesTextView = domainSettingsView.findViewById<TextView>(R.id.current_ip_addresses_textview)
188 // Hide the WebView theme linear layout if the API < 29.
189 if (Build.VERSION.SDK_INT < 29)
190 webViewThemeLinearLayout.visibility = View.GONE
192 // Setup the pinned labels.
193 val cNameLabel = getString(R.string.common_name)
194 val oNameLabel = getString(R.string.organization)
195 val uNameLabel = getString(R.string.organizational_unit)
196 val startDateLabel = getString(R.string.start_date)
197 val endDateLabel = getString(R.string.end_date)
199 // Initialize the database handler.
200 val domainsDatabaseHelper = DomainsDatabaseHelper(requireContext())
202 // Get the database cursor for this ID.
203 val domainCursor = domainsDatabaseHelper.getCursorForId(databaseId)
205 // Move to the first row.
206 domainCursor.moveToFirst()
208 // Save the cursor entries as variables.
209 val domainNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME))
210 val javaScriptInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT))
211 val cookiesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES))
212 val domStorageInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE))
213 val formDataInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA)) // Form data can be remove once the minimum API >= 26.
214 val easyListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST))
215 val easyPrivacyInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY))
216 val fanboysAnnoyanceListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST))
217 val fanboysSocialBlockingListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST))
218 val ultraListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST))
219 val ultraPrivacyInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY))
220 val blockAllThirdPartyRequestsInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS))
221 val currentUserAgentName = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT))
222 val fontSizeInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE))
223 val swipeToRefreshInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH))
224 val webViewThemeInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME))
225 val wideViewportInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WIDE_VIEWPORT))
226 val displayImagesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DISPLAY_IMAGES))
227 val pinnedSslCertificateInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE))
228 val savedSslIssuedToCNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME))
229 val savedSslIssuedToONameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION))
230 val savedSslIssuedToUNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT))
231 val savedSslIssuedByCNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME))
232 val savedSslIssuedByONameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION))
233 val savedSslIssuedByUNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT))
234 val pinnedIpAddressesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_IP_ADDRESSES))
235 val savedIpAddresses = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.IP_ADDRESSES))
237 // Get the SSL dates from the database.
238 val savedSslStartDateLong = domainCursor.getLong(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_START_DATE))
239 val savedSslEndDateLong = domainCursor.getLong(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_END_DATE))
241 // Initialize the saved SSL certificate date variables.
242 var savedSslStartDate: Date? = null
243 var savedSslEndDate: Date? = null
245 // Only get the saved SSL certificate dates from the cursor if they are not set to `0`.
246 if (savedSslStartDateLong != 0L)
247 savedSslStartDate = Date(savedSslStartDateLong)
248 if (savedSslEndDateLong != 0L)
249 savedSslEndDate = Date(savedSslEndDateLong)
251 // Create array adapters for the spinners.
252 val translatedUserAgentArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.translated_domain_settings_user_agent_names, R.layout.spinner_item)
253 val fontSizeArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.font_size_array, R.layout.spinner_item)
254 val swipeToRefreshArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.swipe_to_refresh_array, R.layout.spinner_item)
255 val webViewThemeArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.webview_theme_array, R.layout.spinner_item)
256 val wideViewportArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.wide_viewport_array, R.layout.spinner_item)
257 val displayImagesArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.display_webpage_images_array, R.layout.spinner_item)
259 // Set the drop down view resource on the spinners.
260 translatedUserAgentArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
261 fontSizeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
262 swipeToRefreshArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
263 webViewThemeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
264 wideViewportArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
265 displayImagesArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
267 // Set the array adapters for the spinners.
268 userAgentSpinner.adapter = translatedUserAgentArrayAdapter
269 fontSizeSpinner.adapter = fontSizeArrayAdapter
270 swipeToRefreshSpinner.adapter = swipeToRefreshArrayAdapter
271 webViewThemeSpinner.adapter = webViewThemeArrayAdapter
272 wideViewportSpinner.adapter = wideViewportArrayAdapter
273 displayWebpageImagesSpinner.adapter = displayImagesArrayAdapter
275 // Create a spannable string builder for each TextView that needs multiple colors of text.
276 val savedSslIssuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString)
277 val savedSslIssuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + savedSslIssuedToONameString)
278 val savedSslIssuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + savedSslIssuedToUNameString)
279 val savedSslIssuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedByCNameString)
280 val savedSslIssuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + savedSslIssuedByONameString)
281 val savedSslIssuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + savedSslIssuedByUNameString)
283 // Create the date spannable string builders.
284 val savedSslStartDateStringBuilder: SpannableStringBuilder = if (savedSslStartDate == null)
285 SpannableStringBuilder(startDateLabel)
287 SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslStartDate))
289 val savedSslEndDateStringBuilder: SpannableStringBuilder = if (savedSslEndDate == null)
290 SpannableStringBuilder(endDateLabel)
292 SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslEndDate))
294 // Create the color spans.
295 val blueColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.alt_blue_text))
296 val redColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.red_text))
298 // Set the domain name from the the database cursor.
299 domainNameEditText.setText(domainNameString)
301 // Update the certificates' Common Name color when the domain name text changes.
302 domainNameEditText.addTextChangedListener(object : TextWatcher {
303 override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
307 override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
311 override fun afterTextChanged(s: Editable) {
312 // Get the new domain name.
313 val newDomainName = domainNameEditText.text.toString()
315 // Check the saved SSL certificate against the new domain name.
316 val savedSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, savedSslIssuedToCNameString)
318 // Create a spannable string builder for the saved certificate's Common Name.
319 val savedSslCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString)
321 // Format the saved certificate's Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
322 if (savedSslMatchesNewDomainName) {
323 savedSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
325 savedSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, savedSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
328 // Update the saved SSL issued to CName text view.
329 savedSslIssuedToCNameTextView.text = savedSslCNameStringBuilder
331 // Update the current website certificate if it exists.
332 if (DomainsActivity.sslIssuedToCName != null) {
333 // Check the current website certificate against the new domain name.
334 val currentSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, DomainsActivity.sslIssuedToCName)
336 // Create a spannable string builder for the current website certificate's Common Name.
337 val currentSslCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName)
339 // Format the current certificate Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
340 if (currentSslMatchesNewDomainName) {
341 currentSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
343 currentSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, currentSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
346 // Update the current SSL issued to CName text view.
347 currentSslIssuedToCNameTextView.text = currentSslCNameStringBuilder
352 // Set the switch positions.
353 javaScriptSwitch.isChecked = javaScriptInt == 1
354 cookiesSwitch.isChecked = cookiesInt == 1
355 domStorageSwitch.isChecked = domStorageInt == 1
356 formDataSwitch.isChecked = formDataInt == 1 // Form data can be removed once the minimum API >= 26.
357 easyListSwitch.isChecked = easyListInt == 1
358 easyPrivacySwitch.isChecked = easyPrivacyInt == 1
359 fanboysAnnoyanceListSwitch.isChecked = fanboysAnnoyanceListInt == 1
360 fanboysSocialBlockingListSwitch.isChecked = fanboysSocialBlockingListInt == 1
361 ultraListSwitch.isChecked = ultraListInt == 1
362 ultraPrivacySwitch.isChecked = ultraPrivacyInt == 1
363 blockAllThirdPartyRequestsSwitch.isChecked = blockAllThirdPartyRequestsInt == 1
364 pinnedSslCertificateSwitch.isChecked = pinnedSslCertificateInt == 1
365 pinnedIpAddressesSwitch.isChecked = pinnedIpAddressesInt == 1
367 // Set the switch icon colors.
368 cookiesImageView.isSelected = cookiesInt == 1
369 domStorageImageView.isSelected = domStorageInt == 1
370 formDataImageView.isSelected = formDataInt == 1 // Form data can be removed once the minimum API >= 26.
371 easyListImageView.isSelected = easyListInt == 1
372 easyPrivacyImageView.isSelected = easyPrivacyInt == 1
373 fanboysAnnoyanceListImageView.isSelected = fanboysAnnoyanceListInt == 1
374 fanboysSocialBlockingListImageView.isSelected = fanboysSocialBlockingListInt == 1
375 ultraListImageView.isSelected = ultraListInt == 1
376 ultraPrivacyImageView.isSelected = ultraPrivacyInt == 1
377 blockAllThirdPartyRequestsImageView.isSelected = blockAllThirdPartyRequestsInt == 1
378 pinnedSslCertificateImageView.isSelected = pinnedSslCertificateInt == 1
379 pinnedIpAddressesImageView.isSelected = pinnedIpAddressesInt == 1
381 // Set the JavaScript icon.
382 if (javaScriptInt == 1)
383 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null))
385 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null))
387 // Set the DOM storage switch status based on the JavaScript status.
388 domStorageSwitch.isEnabled = (javaScriptInt == 1)
390 // Set the DOM storage icon ghosted status based on the JavaScript status.
391 domStorageImageView.isEnabled = (javaScriptInt == 1)
393 // Set the form data visibility. Form data can be removed once the minimum API >= 26.
394 if (Build.VERSION.SDK_INT >= 26) {
395 // Hide the form data image view and switch.
396 formDataImageView.visibility = View.GONE
397 formDataSwitch.visibility = View.GONE
400 // Set Fanboy's Social Blocking List switch status based on the Annoyance List status.
401 fanboysSocialBlockingListSwitch.isEnabled = (fanboysAnnoyanceListInt == 0)
403 // Set the Social Blocking List icon ghosted status based on the Annoyance List status.
404 fanboysSocialBlockingListImageView.isEnabled = (fanboysAnnoyanceListInt == 0)
406 // Inflated a WebView to get the default user agent.
407 // `@SuppressLint("InflateParams")` removes the warning about using `null` as the `ViewGroup`, which in this case makes sense because the bare WebView should not be displayed on the screen.
408 @SuppressLint("InflateParams") val bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false)
409 val bareWebView = bareWebViewLayout.findViewById<WebView>(R.id.bare_webview)
410 val webViewDefaultUserAgentString = bareWebView.settings.userAgentString
412 // Get a handle for the user agent array adapter. This array does not contain the `System default` entry.
413 val userAgentNamesArray = ArrayAdapter.createFromResource(requireContext(), R.array.user_agent_names, R.layout.spinner_item)
415 // Get the positions of the user agent and the default user agent.
416 val userAgentArrayPosition = userAgentNamesArray.getPosition(currentUserAgentName)
417 val defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName)
419 // Get a handle for the user agent data array. This array does not contain the `System default` entry.
420 val userAgentDataArray = resources.getStringArray(R.array.user_agent_data)
422 // Set the user agent text.
423 if (currentUserAgentName == getString(R.string.system_default_user_agent)) { // Use the system default user agent.
424 // Set the user agent according to the system default.
425 when (defaultUserAgentArrayPosition) {
426 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
427 UNRECOGNIZED_USER_AGENT -> userAgentTextView.text = defaultUserAgentName
429 // Display the WebView default user agent.
430 SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentTextView.text = webViewDefaultUserAgentString
432 // Display the custom user agent.
433 SETTINGS_CUSTOM_USER_AGENT -> userAgentTextView.text = defaultCustomUserAgentString
435 // Get the user agent string from the user agent data array.
436 else -> userAgentTextView.text = userAgentDataArray[defaultUserAgentArrayPosition]
438 } else if (userAgentArrayPosition == UNRECOGNIZED_USER_AGENT || currentUserAgentName == getString(R.string.custom_user_agent)) {
439 // A custom user agent is stored in the current user agent name. The second check is necessary in case the user did not change the default custom text.
440 // Set the user agent spinner to `Custom user agent`.
441 userAgentSpinner.setSelection(DOMAINS_CUSTOM_USER_AGENT)
443 // Hide the user agent text view.
444 userAgentTextView.visibility = View.GONE
446 // Show the custom user agent edit text and set the current user agent name as the text.
447 customUserAgentEditText.visibility = View.VISIBLE
448 customUserAgentEditText.setText(currentUserAgentName)
449 } else { // The user agent name contains one of the canonical user agents.
450 // Set the user agent spinner selection. The spinner has one more entry at the beginning than the user agent data array, so the position must be incremented.
451 userAgentSpinner.setSelection(userAgentArrayPosition + 1)
453 // Show the user agent text view.
454 userAgentTextView.visibility = View.VISIBLE
456 // Hide the custom user agent edit text.
457 customUserAgentEditText.visibility = View.GONE
459 // Set the user agent text.
460 if (userAgentArrayPosition == DOMAINS_WEBVIEW_DEFAULT_USER_AGENT) { // The WebView default user agent is selected.
461 // Display the WebView default user agent.
462 userAgentTextView.text = webViewDefaultUserAgentString
463 } else { // A user agent besides the default is selected.
464 // Get the user agent string from the user agent data array. The spinner has one more entry at the beginning than the user agent data array, so the position must be incremented.
465 userAgentTextView.text = userAgentDataArray[userAgentArrayPosition + 1]
469 // Open the user agent spinner when the text view is clicked.
470 userAgentTextView.setOnClickListener { userAgentSpinner.performClick() }
472 // Display the font size settings.
473 if (fontSizeInt == 0) { // `0` is the code for system default font size.
474 // Set the font size to the system default.
475 fontSizeSpinner.setSelection(0)
477 // Show the default font size text view.
478 defaultFontSizeTextView.visibility = View.VISIBLE
480 // Hide the custom font size edit text.
481 customFontSizeEditText.visibility = View.GONE
483 // Set the default font size as the text of the custom font size edit text. This way, if the user switches to custom it will already be populated.
484 customFontSizeEditText.setText(defaultFontSizeString)
485 } else { // A custom font size is selected.
486 // Set the spinner to the custom font size.
487 fontSizeSpinner.setSelection(1)
489 // Hide the default font size text view.
490 defaultFontSizeTextView.visibility = View.GONE
492 // Show the custom font size edit text.
493 customFontSizeEditText.visibility = View.GONE
495 // Set the custom font size.
496 customFontSizeEditText.setText(fontSizeInt.toString())
499 // Initialize the default font size percentage string.
500 val defaultFontSizePercentageString = "$defaultFontSizeString%"
502 // Set the default font size text in the text view.
503 defaultFontSizeTextView.text = defaultFontSizePercentageString
505 // Open the font size spinner when the text view is clicked.
506 defaultFontSizeTextView.setOnClickListener { fontSizeSpinner.performClick() }
508 // Select the swipe-to-refresh selection in the spinner.
509 swipeToRefreshSpinner.setSelection(swipeToRefreshInt)
511 // Set the swipe-to-refresh text.
512 if (defaultSwipeToRefresh)
513 swipeToRefreshTextView.text = swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
515 swipeToRefreshTextView.text = swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
517 // Set the swipe-to-refresh icon and text view settings.
518 when (swipeToRefreshInt) {
519 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
520 // Set the icon color.
521 swipeToRefreshImageView.isSelected = defaultSwipeToRefresh
523 // Show the swipe-to-refresh text view.
524 swipeToRefreshTextView.visibility = View.VISIBLE
527 DomainsDatabaseHelper.ENABLED -> {
528 // Set the icon color.
529 swipeToRefreshImageView.isSelected = true
531 // Hide the swipe-to-refresh text view.
532 swipeToRefreshTextView.visibility = View.GONE
535 DomainsDatabaseHelper.DISABLED -> {
536 // Set the icon color.
537 swipeToRefreshImageView.isSelected = false
539 // Hide the swipe-to-refresh text view.
540 swipeToRefreshTextView.visibility = View.GONE
544 // Open the swipe-to-refresh spinner when the text view is clicked.
545 swipeToRefreshTextView.setOnClickListener { swipeToRefreshSpinner.performClick() }
547 // Get the WebView theme string arrays.
548 val webViewThemeStringArray = resources.getStringArray(R.array.webview_theme_array)
549 val webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values)
551 // Get the WebView theme entry number that matches the current WebView theme.
552 val appWebViewThemeEntryNumber = when (defaultWebViewTheme) {
553 webViewThemeEntryValuesStringArray[1] -> { 1 } // The light theme is selected.
554 webViewThemeEntryValuesStringArray[2] -> { 2 } // The dark theme is selected.
555 else -> { 0 } // The system default theme is selected.
558 // Select the WebView theme in the spinner.
559 webViewThemeSpinner.setSelection(webViewThemeInt)
561 // Set the WebView theme text.
562 if (appWebViewThemeEntryNumber == DomainsDatabaseHelper.SYSTEM_DEFAULT) { // The app WebView theme is system default.
563 // Set the text according to the current UI theme.
564 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
565 webViewThemeTextView.text = webViewThemeStringArray[DomainsDatabaseHelper.LIGHT_THEME]
567 webViewThemeTextView.text = webViewThemeStringArray[DomainsDatabaseHelper.DARK_THEME]
568 } else { // The app WebView theme is not system default.
569 // Set the text according to the app WebView theme.
570 webViewThemeTextView.text = webViewThemeStringArray[appWebViewThemeEntryNumber]
573 // Set the WebView theme icon and text visibility.
574 when (webViewThemeInt) {
575 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
576 // Set the icon color.
577 when (appWebViewThemeEntryNumber) {
578 DomainsDatabaseHelper.SYSTEM_DEFAULT -> webViewThemeImageView.isSelected = (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
579 DomainsDatabaseHelper.LIGHT_THEME -> webViewThemeImageView.isSelected = true
580 DomainsDatabaseHelper.DARK_THEME -> webViewThemeImageView.isSelected = false
583 // Show the WebView theme text view.
584 webViewThemeTextView.visibility = View.VISIBLE
587 DomainsDatabaseHelper.LIGHT_THEME -> {
588 // Set the icon color.
589 webViewThemeImageView.isSelected = true
591 // Hide the WebView theme text view.
592 webViewThemeTextView.visibility = View.GONE
595 DomainsDatabaseHelper.DARK_THEME -> {
596 // Set the icon color.
597 webViewThemeImageView.isSelected = false
599 // Hide the WebView theme text view.
600 webViewThemeTextView.visibility = View.GONE
604 // Open the WebView theme spinner when the text view is clicked.
605 webViewThemeTextView.setOnClickListener { webViewThemeSpinner.performClick() }
607 // Select the wide viewport in the spinner.
608 wideViewportSpinner.setSelection(wideViewportInt)
610 // Set the default wide viewport text.
611 if (defaultWideViewport)
612 wideViewportTextView.text = wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
613 else wideViewportTextView.text = wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
615 // Set the wide viewport icon and text view settings.
616 when (wideViewportInt) {
617 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
618 // Set the icon color.
619 wideViewportImageView.isSelected = defaultWideViewport
621 // Show the wide viewport text view.
622 wideViewportTextView.visibility = View.VISIBLE
625 DomainsDatabaseHelper.ENABLED -> {
626 // Set the icon color.
627 wideViewportImageView.isSelected = true
629 // Hide the wide viewport text view.
630 wideViewportTextView.visibility = View.GONE
633 DomainsDatabaseHelper.DISABLED -> {
634 // Set the icon color.
635 wideViewportImageView.isSelected = false
637 // Hide the wide viewport text view.
638 wideViewportTextView.visibility = View.GONE
642 // Open the wide viewport spinner when the text view is clicked.
643 wideViewportTextView.setOnClickListener { wideViewportSpinner.performClick() }
645 // Display the website images mode in the spinner.
646 displayWebpageImagesSpinner.setSelection(displayImagesInt)
648 // Set the default display images text.
649 if (defaultDisplayWebpageImages)
650 displayImagesTextView.text = displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
652 displayImagesTextView.text = displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
654 // Set the display website images icon and text view settings.
655 when (displayImagesInt) {
656 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
657 // Set the icon color.
658 displayWebpageImagesImageView.isSelected = defaultDisplayWebpageImages
660 // Show the display images text view.
661 displayImagesTextView.visibility = View.VISIBLE
664 DomainsDatabaseHelper.ENABLED -> {
665 // Set the icon color.
666 displayWebpageImagesImageView.isSelected = true
668 // Hide the display images text view.
669 displayImagesTextView.visibility = View.GONE
672 DomainsDatabaseHelper.DISABLED -> {
673 // Set the icon color.
674 displayWebpageImagesImageView.isSelected = false
676 // Hide the display images text view.
677 displayImagesTextView.visibility = View.GONE
681 // Open the display images spinner when the text view is clicked.
682 displayImagesTextView.setOnClickListener { displayWebpageImagesSpinner.performClick() }
684 // Store the current date.
685 val currentDate = Calendar.getInstance().time
687 // Setup the string builders to display the general certificate information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
688 savedSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, savedSslIssuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
689 savedSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, savedSslIssuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
690 savedSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslIssuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
691 savedSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, savedSslIssuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
692 savedSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, savedSslIssuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
694 // Check the certificate Common Name against the domain name.
695 val savedSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslIssuedToCNameString)
697 // Format the issued to Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
698 if (savedSslCommonNameMatchesDomainName)
699 savedSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
701 savedSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, savedSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
703 // Format the start date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
704 if (savedSslStartDate != null && savedSslStartDate.after(currentDate)) // The certificate start date is in the future.
705 savedSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, savedSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
706 else // The certificate start date is in the past.
707 savedSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, savedSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
709 // Format the end date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
710 if (savedSslEndDate != null && savedSslEndDate.before(currentDate)) // The certificate end date is in the past.
711 savedSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, savedSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
712 else // The certificate end date is in the future.
713 savedSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, savedSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
715 // Display the saved website SSL certificate strings.
716 savedSslIssuedToCNameTextView.text = savedSslIssuedToCNameStringBuilder
717 savedSslIssuedToONameTextView.text = savedSslIssuedToONameStringBuilder
718 savedSslIssuedToUNameTextView.text = savedSslIssuedToUNameStringBuilder
719 savedSslIssuedByCNameTextView.text = savedSslIssuedByCNameStringBuilder
720 savedSslIssuedByONameTextView.text = savedSslIssuedByONameStringBuilder
721 savedSslIssuedByUNameTextView.text = savedSslIssuedByUNameStringBuilder
722 savedSslStartDateTextView.text = savedSslStartDateStringBuilder
723 savedSslEndDateTextView.text = savedSslEndDateStringBuilder
725 // Populate the current website SSL certificate if there is one.
726 if (DomainsActivity.sslIssuedToCName != null) {
727 // Get dates from the raw long values.
728 val currentSslStartDate = Date(DomainsActivity.sslStartDateLong)
729 val currentSslEndDate = Date(DomainsActivity.sslEndDateLong)
731 // Create a spannable string builder for each text view that needs multiple colors of text.
732 val currentSslIssuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName)
733 val currentSslIssuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedToOName)
734 val currentSslIssuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedToUName)
735 val currentSslIssuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedByCName)
736 val currentSslIssuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedByOName)
737 val currentSslIssuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedByUName)
738 val currentSslStartDateStringBuilder = SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslStartDate))
739 val currentSslEndDateStringBuilder = SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslEndDate))
741 // Setup the string builders to display the general certificate information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
742 currentSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, currentSslIssuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
743 currentSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, currentSslIssuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
744 currentSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslIssuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
745 currentSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, currentSslIssuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
746 currentSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, currentSslIssuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
748 // Check the certificate Common Name against the domain name.
749 val currentSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, DomainsActivity.sslIssuedToCName)
751 // Format the issued to Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
752 if (currentSslCommonNameMatchesDomainName)
753 currentSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
755 currentSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, currentSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
757 // Format the start date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
758 if (currentSslStartDate.after(currentDate)) // The certificate start date is in the future.
759 currentSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, currentSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
760 else // The certificate start date is in the past.
761 currentSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, currentSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
763 // Format the end date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
764 if (currentSslEndDate.before(currentDate)) // The certificate end date is in the past.
765 currentSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, currentSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
766 else // The certificate end date is in the future.
767 currentSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, currentSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
769 // Display the current website SSL certificate strings.
770 currentSslIssuedToCNameTextView.text = currentSslIssuedToCNameStringBuilder
771 currentSslIssuedToONameTextView.text = currentSslIssuedToONameStringBuilder
772 currentSslIssuedToUNameTextView.text = currentSslIssuedToUNameStringBuilder
773 currentSslIssuedByCNameTextView.text = currentSslIssuedByCNameStringBuilder
774 currentSslIssuedByONameTextView.text = currentSslIssuedByONameStringBuilder
775 currentSslIssuedByUNameTextView.text = currentSslIssuedByUNameStringBuilder
776 currentSslStartDateTextView.text = currentSslStartDateStringBuilder
777 currentSslEndDateTextView.text = currentSslEndDateStringBuilder
780 // Set the initial display status of the SSL certificates card views.
781 if (pinnedSslCertificateSwitch.isChecked) { // An SSL certificate is pinned.
782 // Set the visibility of the saved SSL certificate.
783 if (savedSslIssuedToCNameString == null)
784 savedSslCardView.visibility = View.GONE
786 savedSslCardView.visibility = View.VISIBLE
788 // Set the visibility of the current website SSL certificate.
789 if (DomainsActivity.sslIssuedToCName == null) { // There is no current SSL certificate.
790 // Hide the SSL certificate.
791 currentSslCardView.visibility = View.GONE
793 // Show the instruction.
794 noCurrentWebsiteCertificateTextView.visibility = View.VISIBLE
795 } else { // There is a current SSL certificate.
796 // Show the SSL certificate.
797 currentSslCardView.visibility = View.VISIBLE
799 // Hide the instruction.
800 noCurrentWebsiteCertificateTextView.visibility = View.GONE
803 // Set the status of the radio buttons and the card view backgrounds.
804 if (savedSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is displayed.
805 // Check the saved SSL certificate radio button.
806 savedSslCertificateRadioButton.isChecked = true
808 // Uncheck the current website SSL certificate radio button.
809 currentWebsiteCertificateRadioButton.isChecked = false
811 // Darken the background of the current website SSL certificate linear layout.
812 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
813 } else if (currentSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is hidden but the current website SSL certificate is visible.
814 // Check the current website SSL certificate radio button.
815 currentWebsiteCertificateRadioButton.isChecked = true
817 // Uncheck the saved SSL certificate radio button.
818 savedSslCertificateRadioButton.isChecked = false
819 } else { // Neither SSL certificate is visible.
820 // Uncheck both radio buttons.
821 savedSslCertificateRadioButton.isChecked = false
822 currentWebsiteCertificateRadioButton.isChecked = false
824 } else { // An SSL certificate is not pinned.
825 // Hide the SSl certificates and instructions.
826 savedSslCardView.visibility = View.GONE
827 currentSslCardView.visibility = View.GONE
828 noCurrentWebsiteCertificateTextView.visibility = View.GONE
830 // Uncheck the radio buttons.
831 savedSslCertificateRadioButton.isChecked = false
832 currentWebsiteCertificateRadioButton.isChecked = false
835 // Populate the saved and current IP addresses.
836 savedIpAddressesTextView.text = savedIpAddresses
837 currentIpAddressesTextView.text = DomainsActivity.currentIpAddresses
839 // Set the initial display status of the IP addresses card views.
840 if (pinnedIpAddressesSwitch.isChecked) { // IP addresses are pinned.
841 // Set the visibility of the saved IP addresses.
842 if (savedIpAddresses == null) // There are no saved IP addresses.
843 savedIpAddressesCardView.visibility = View.GONE
844 else // There are saved IP addresses.
845 savedIpAddressesCardView.visibility = View.VISIBLE
847 // Set the visibility of the current IP addresses.
848 currentIpAddressesCardView.visibility = View.VISIBLE
850 // Set the status of the radio buttons and the card view backgrounds.
851 if (savedIpAddressesCardView.visibility == View.VISIBLE) { // The saved IP addresses are displayed.
852 // Check the saved IP addresses radio button.
853 savedIpAddressesRadioButton.isChecked = true
855 // Uncheck the current IP addresses radio button.
856 currentIpAddressesRadioButton.isChecked = false
858 // Darken the background of the current IP addresses linear layout.
859 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
860 } else { // The saved IP addresses are hidden.
861 // Check the current IP addresses radio button.
862 currentIpAddressesRadioButton.isChecked = true
864 // Uncheck the saved IP addresses radio button.
865 savedIpAddressesRadioButton.isChecked = false
867 } else { // IP addresses are not pinned.
868 // Hide the IP addresses card views.
869 savedIpAddressesCardView.visibility = View.GONE
870 currentIpAddressesCardView.visibility = View.GONE
872 // Uncheck the radio buttons.
873 savedIpAddressesRadioButton.isChecked = false
874 currentIpAddressesRadioButton.isChecked = false
878 // Set the JavaScript switch listener.
879 javaScriptSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
880 // Update the JavaScript icon.
882 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null))
884 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null))
886 // Set the DOM storage switch status.
887 domStorageSwitch.isEnabled = isChecked
889 // Set the DOM storage ghosted icon status.
890 domStorageImageView.isEnabled = isChecked
893 // Set the cookies switch listener.
894 cookiesSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
895 // Update the icon color.
896 cookiesImageView.isSelected = isChecked
899 // Set the DOM Storage switch listener.
900 domStorageSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
901 // Update the icon color.
902 domStorageImageView.isSelected = isChecked
905 // Set the form data switch listener. It can be removed once the minimum API >= 26.
906 if (Build.VERSION.SDK_INT < 26) {
907 formDataSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
908 // Update the icon color.
909 formDataImageView.isSelected = isChecked
913 // Set the EasyList switch listener.
914 easyListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
915 // Update the icon color.
916 easyListImageView.isSelected = isChecked
919 // Set the EasyPrivacy switch listener.
920 easyPrivacySwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
921 // Update the icon color.
922 easyPrivacyImageView.isSelected = isChecked
925 // Set the Fanboy's Annoyance List switch listener.
926 fanboysAnnoyanceListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
927 // Update the icon color.
928 fanboysAnnoyanceListImageView.isSelected = isChecked
930 // Set Fanboy's Social Blocking List switch position.
931 fanboysSocialBlockingListSwitch.isEnabled = !isChecked
933 // Set the Social Blocking List icon ghosted status.
934 fanboysSocialBlockingListImageView.isEnabled = !isChecked
937 // Set the Fanboy's Social Blocking List switch listener.
938 fanboysSocialBlockingListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
939 // Update the icon color.
940 fanboysSocialBlockingListImageView.isSelected = isChecked
943 // Set the UltraList switch listener.
944 ultraListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
945 // Update the icon color.
946 ultraListImageView.isSelected = isChecked
949 // Set the UltraPrivacy switch listener.
950 ultraPrivacySwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
951 // Update the icon color.
952 ultraPrivacyImageView.isSelected = isChecked
955 // Set the block all third-party requests switch listener.
956 blockAllThirdPartyRequestsSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
957 // Update the icon color.
958 blockAllThirdPartyRequestsImageView.isSelected = isChecked
961 // Set the user agent spinner listener.
962 userAgentSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
963 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
964 // Set the new user agent.
966 DOMAINS_SYSTEM_DEFAULT_USER_AGENT -> {
967 // Show the user agent text view.
968 userAgentTextView.visibility = View.VISIBLE
970 // Hide the custom user agent edit text.
971 customUserAgentEditText.visibility = View.GONE
973 // Set the user text.
974 when (defaultUserAgentArrayPosition) {
975 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
976 UNRECOGNIZED_USER_AGENT -> userAgentTextView.text = defaultUserAgentName
978 // Display the `WebView` default user agent.
979 SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentTextView.text = webViewDefaultUserAgentString
981 // Display the custom user agent.
982 SETTINGS_CUSTOM_USER_AGENT -> userAgentTextView.text = defaultCustomUserAgentString
984 // Get the user agent string from the user agent data array.
985 else -> userAgentTextView.text = userAgentDataArray[defaultUserAgentArrayPosition]
989 DOMAINS_WEBVIEW_DEFAULT_USER_AGENT -> {
990 // Show the user agent text view.
991 userAgentTextView.visibility = View.VISIBLE
993 // Set the user agent text.
994 userAgentTextView.text = webViewDefaultUserAgentString
996 // Hide the custom user agent EditTex.
997 customUserAgentEditText.visibility = View.GONE
1000 DOMAINS_CUSTOM_USER_AGENT -> {
1001 // Hide the user agent TextView.
1002 userAgentTextView.visibility = View.GONE
1004 // Show the custom user agent edit text.
1005 customUserAgentEditText.visibility = View.VISIBLE
1007 // Set the current user agent name as the text.
1008 customUserAgentEditText.setText(currentUserAgentName)
1012 // Show the user agent text view.
1013 userAgentTextView.visibility = View.VISIBLE
1015 // Set the text from the user agent data array, which has one less entry than the spinner, so the position must be decremented.
1016 userAgentTextView.text = userAgentDataArray[position - 1]
1018 // Hide the custom user agent edit text.
1019 customUserAgentEditText.visibility = View.GONE
1024 override fun onNothingSelected(parent: AdapterView<*>?) {
1029 // Set the font size spinner listener.
1030 fontSizeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1031 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1032 // Update the font size display options.
1033 if (position == 0) { // The system default font size has been selected.
1034 // Show the default font size text view.
1035 defaultFontSizeTextView.visibility = View.VISIBLE
1037 // Hide the custom font size edit text.
1038 customFontSizeEditText.visibility = View.GONE
1039 } else { // A custom font size has been selected.
1040 // Hide the default font size text view.
1041 defaultFontSizeTextView.visibility = View.GONE
1043 // Show the custom font size edit text.
1044 customFontSizeEditText.visibility = View.VISIBLE
1048 override fun onNothingSelected(parent: AdapterView<*>?) {
1053 // Set the swipe-to-refresh spinner listener.
1054 swipeToRefreshSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1055 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1056 // Update the icon and the visibility of the text view.
1058 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1059 // Set the icon color.
1060 swipeToRefreshImageView.isSelected = defaultSwipeToRefresh
1062 // Show the swipe-to-refresh text view.
1063 swipeToRefreshTextView.visibility = View.VISIBLE
1066 DomainsDatabaseHelper.ENABLED -> {
1067 // Set the icon color.
1068 swipeToRefreshImageView.isSelected = true
1070 // Hide the swipe-to-refresh text view.
1071 swipeToRefreshTextView.visibility = View.GONE
1074 DomainsDatabaseHelper.DISABLED -> {
1075 // Set the icon color.
1076 swipeToRefreshImageView.isSelected = false
1078 // Hide the swipe-to-refresh text view.
1079 swipeToRefreshTextView.visibility = View.GONE
1084 override fun onNothingSelected(parent: AdapterView<*>?) {
1089 // Set the WebView theme spinner listener.
1090 webViewThemeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1091 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1092 // Update the icon and the visibility of the WebView theme text view.
1094 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1095 // Set the icon color.
1096 when (appWebViewThemeEntryNumber) {
1097 DomainsDatabaseHelper.SYSTEM_DEFAULT -> webViewThemeImageView.isSelected = (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
1098 DomainsDatabaseHelper.LIGHT_THEME -> webViewThemeImageView.isSelected = true
1099 DomainsDatabaseHelper.DARK_THEME -> webViewThemeImageView.isSelected = false
1102 // Show the WebView theme text view.
1103 webViewThemeTextView.visibility = View.VISIBLE
1106 DomainsDatabaseHelper.LIGHT_THEME -> {
1107 // Set the icon color.
1108 webViewThemeImageView.isSelected = true
1110 // Hide the WebView theme text view.
1111 webViewThemeTextView.visibility = View.GONE
1114 DomainsDatabaseHelper.DARK_THEME -> {
1115 // Set the icon color.
1116 webViewThemeImageView.isSelected = false
1118 // Hide the WebView theme text view.
1119 webViewThemeTextView.visibility = View.GONE
1124 override fun onNothingSelected(parent: AdapterView<*>?) {
1129 // Set the wide viewport spinner listener.
1130 wideViewportSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1131 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1132 // Update the icon and the visibility of the wide viewport text view.
1134 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1135 // Set the icon color.
1136 wideViewportImageView.isSelected = defaultWideViewport
1138 // Show the wide viewport text view.
1139 wideViewportTextView.visibility = View.VISIBLE
1142 DomainsDatabaseHelper.ENABLED -> {
1143 // Set the icon color.
1144 wideViewportImageView.isSelected = true
1146 // Hide the wide viewport text view.
1147 wideViewportTextView.visibility = View.GONE
1150 DomainsDatabaseHelper.DISABLED -> {
1151 // Set the icon color.
1152 wideViewportImageView.isSelected = false
1154 // Hid ethe wide viewport text view.
1155 wideViewportTextView.visibility = View.GONE
1160 override fun onNothingSelected(parent: AdapterView<*>?) {
1165 // Set the display webpage images spinner listener.
1166 displayWebpageImagesSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1167 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1168 // Update the icon and the visibility of the display images text view.
1170 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1171 // Set the icon color.
1172 displayWebpageImagesImageView.isSelected = defaultDisplayWebpageImages
1174 // Show the display images text view.
1175 displayImagesTextView.visibility = View.VISIBLE
1178 DomainsDatabaseHelper.ENABLED -> {
1179 // Set the icon color.
1180 displayWebpageImagesImageView.isSelected = true
1182 // Hide the display images text view.
1183 displayImagesTextView.visibility = View.GONE
1186 DomainsDatabaseHelper.DISABLED -> {
1187 // Set the icon color.
1188 displayWebpageImagesImageView.isSelected = false
1190 // Hide the display images text view.
1191 displayImagesTextView.visibility = View.GONE
1196 override fun onNothingSelected(parent: AdapterView<*>?) {
1201 // Set the pinned SSL certificate switch listener.
1202 pinnedSslCertificateSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1203 // Update the icon color.
1204 pinnedSslCertificateImageView.isSelected = isChecked
1206 // Update the views.
1207 if (isChecked) { // SSL certificate pinning is enabled.
1208 // Update the visibility of the saved SSL certificate.
1209 if (savedSslIssuedToCNameString == null)
1210 savedSslCardView.visibility = View.GONE
1212 savedSslCardView.visibility = View.VISIBLE
1214 // Update the visibility of the current website SSL certificate.
1215 if (DomainsActivity.sslIssuedToCName == null) {
1216 // Hide the SSL certificate.
1217 currentSslCardView.visibility = View.GONE
1219 // Show the instruction.
1220 noCurrentWebsiteCertificateTextView.visibility = View.VISIBLE
1222 // Show the SSL certificate.
1223 currentSslCardView.visibility = View.VISIBLE
1225 // Hide the instruction.
1226 noCurrentWebsiteCertificateTextView.visibility = View.GONE
1229 // Set the status of the radio buttons.
1230 if (savedSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is displayed.
1231 // Check the saved SSL certificate radio button.
1232 savedSslCertificateRadioButton.isChecked = true
1234 // Uncheck the current website SSL certificate radio button.
1235 currentWebsiteCertificateRadioButton.isChecked = false
1237 // Set the background of the saved SSL certificate linear layout to be transparent.
1238 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1240 // Darken the background of the current website SSL certificate linear layout according to the theme.
1241 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1243 // Scroll to the current website SSL certificate card.
1244 savedSslCardView.parent.requestChildFocus(savedSslCardView, savedSslCardView)
1245 } else if (currentSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1246 // Check the current website SSL certificate radio button.
1247 currentWebsiteCertificateRadioButton.isChecked = true
1249 // Uncheck the saved SSL certificate radio button.
1250 savedSslCertificateRadioButton.isChecked = false
1252 // Set the background of the current website SSL certificate linear layout to be transparent.
1253 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1255 // Darken the background of the saved SSL certificate linear layout according to the theme.
1256 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1258 // Scroll to the current website SSL certificate card.
1259 currentSslCardView.parent.requestChildFocus(currentSslCardView, currentSslCardView)
1260 } else { // Neither SSL certificate is visible.
1261 // Uncheck both radio buttons.
1262 savedSslCertificateRadioButton.isChecked = false
1263 currentWebsiteCertificateRadioButton.isChecked = false
1265 // Scroll to the current website SSL certificate card.
1266 noCurrentWebsiteCertificateTextView.parent.requestChildFocus(noCurrentWebsiteCertificateTextView, noCurrentWebsiteCertificateTextView)
1268 } else { // SSL certificate pinning is disabled.
1269 // Hide the SSl certificates and instructions.
1270 savedSslCardView.visibility = View.GONE
1271 currentSslCardView.visibility = View.GONE
1272 noCurrentWebsiteCertificateTextView.visibility = View.GONE
1274 // Uncheck the radio buttons.
1275 savedSslCertificateRadioButton.isChecked = false
1276 currentWebsiteCertificateRadioButton.isChecked = false
1280 // Set the saved SSL card view listener.
1281 savedSslCardView.setOnClickListener {
1282 // Check the saved SSL certificate radio button.
1283 savedSslCertificateRadioButton.isChecked = true
1285 // Uncheck the current website SSL certificate radio button.
1286 currentWebsiteCertificateRadioButton.isChecked = false
1288 // Set the background of the saved SSL certificate linear layout to be transparent.
1289 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1291 // Darken the background of the current website SSL certificate linear layout.
1292 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1295 // Set the saved SSL certificate radio button listener.
1296 savedSslCertificateRadioButton.setOnClickListener {
1297 // Check the saved SSL certificate radio button.
1298 savedSslCertificateRadioButton.isChecked = true
1300 // Uncheck the current website SSL certificate radio button.
1301 currentWebsiteCertificateRadioButton.isChecked = false
1303 // Set the background of the saved SSL certificate linear layout to be transparent.
1304 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1306 // Darken the background of the current website SSL certificate linear layout.
1307 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1310 // Set the current SSL card view listener.
1311 currentSslCardView.setOnClickListener {
1312 // Check the current website SSL certificate radio button.
1313 currentWebsiteCertificateRadioButton.isChecked = true
1315 // Uncheck the saved SSL certificate radio button.
1316 savedSslCertificateRadioButton.isChecked = false
1318 // Set the background of the current website SSL certificate linear layout to be transparent.
1319 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1321 // Darken the background of the saved SSL certificate linear layout.
1322 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1325 // Set the current website certificate radio button listener.
1326 currentWebsiteCertificateRadioButton.setOnClickListener {
1327 // Check the current website SSL certificate radio button.
1328 currentWebsiteCertificateRadioButton.isChecked = true
1330 // Uncheck the saved SSL certificate radio button.
1331 savedSslCertificateRadioButton.isChecked = false
1333 // Set the background of the current website SSL certificate linear layout to be transparent.
1334 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1336 // Darken the background of the saved SSL certificate linear layout.
1337 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1340 // Set the pinned IP addresses switch listener.
1341 pinnedIpAddressesSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1342 // Update the icon color.
1343 pinnedIpAddressesImageView.isSelected = isChecked
1345 // Update the views.
1346 if (isChecked) { // IP addresses pinning is enabled.
1347 // Update the visibility of the saved IP addresses card view.
1348 if (savedIpAddresses == null)
1349 savedIpAddressesCardView.visibility = View.GONE
1351 savedIpAddressesCardView.visibility = View.VISIBLE
1353 // Show the current IP addresses card view.
1354 currentIpAddressesCardView.visibility = View.VISIBLE
1356 // Set the status of the radio buttons.
1357 if (savedIpAddressesCardView.visibility == View.VISIBLE) { // The saved IP addresses are visible.
1358 // Check the saved IP addresses radio button.
1359 savedIpAddressesRadioButton.isChecked = true
1361 // Uncheck the current IP addresses radio button.
1362 currentIpAddressesRadioButton.isChecked = false
1364 // Set the background of the saved IP addresses linear layout to be transparent.
1365 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1367 // Darken the background of the current IP addresses linear layout.
1368 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1369 } else { // The saved IP addresses are not visible.
1370 // Check the current IP addresses radio button.
1371 currentIpAddressesRadioButton.isChecked = true
1373 // Uncheck the saved IP addresses radio button.
1374 savedIpAddressesRadioButton.isChecked = false
1376 // Set the background of the current IP addresses linear layout to be transparent.
1377 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1379 // Darken the background of the saved IP addresses linear layout.
1380 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1383 // Scroll to the bottom of the card views.
1384 currentIpAddressesCardView.parent.requestChildFocus(currentIpAddressesCardView, currentIpAddressesCardView)
1385 } else { // IP addresses pinning is disabled.
1386 // Hide the IP addresses card views.
1387 savedIpAddressesCardView.visibility = View.GONE
1388 currentIpAddressesCardView.visibility = View.GONE
1390 // Uncheck the radio buttons.
1391 savedIpAddressesRadioButton.isChecked = false
1392 currentIpAddressesRadioButton.isChecked = false
1396 // Set the saved IP addresses card view listener.
1397 savedIpAddressesCardView.setOnClickListener {
1398 // Check the saved IP addresses radio button.
1399 savedIpAddressesRadioButton.isChecked = true
1401 // Uncheck the current website IP addresses radio button.
1402 currentIpAddressesRadioButton.isChecked = false
1404 // Set the background of the saved IP addresses linear layout to be transparent.
1405 savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1407 // Darken the background of the current IP addresses linear layout.
1408 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1411 // Set the saved IP addresses radio button listener.
1412 savedIpAddressesRadioButton.setOnClickListener {
1413 // Check the saved IP addresses radio button.
1414 savedIpAddressesRadioButton.isChecked = true
1416 // Uncheck the current website IP addresses radio button.
1417 currentIpAddressesRadioButton.isChecked = false
1419 // Set the background of the saved IP addresses linear layout to be transparent.
1420 savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1422 // Darken the background of the current IP addresses linear layout.
1423 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1426 // Set the current IP addresses card view listener.
1427 currentIpAddressesCardView.setOnClickListener {
1428 // Check the current IP addresses radio button.
1429 currentIpAddressesRadioButton.isChecked = true
1431 // Uncheck the saved IP addresses radio button.
1432 savedIpAddressesRadioButton.isChecked = false
1434 // Set the background of the current IP addresses linear layout to be transparent.
1435 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1437 // Darken the background of the saved IP addresses linear layout.
1438 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1441 // Set the current IP addresses radio button listener.
1442 currentIpAddressesRadioButton.setOnClickListener {
1443 // Check the current IP addresses radio button.
1444 currentIpAddressesRadioButton.isChecked = true
1446 // Uncheck the saved IP addresses radio button.
1447 savedIpAddressesRadioButton.isChecked = false
1449 // Set the background of the current IP addresses linear layout to be transparent.
1450 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1452 // Darken the background of the saved IP addresses linear layout.
1453 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1456 // Set the scroll Y.
1457 domainSettingsScrollView.post { domainSettingsScrollView.scrollY = scrollY }
1459 // Return the domain settings view.
1460 return domainSettingsView
1463 private fun checkDomainNameAgainstCertificate(domainName: String?, certificateCommonName: String?): Boolean {
1464 // Initialize the domain names match tracker.
1465 var domainNamesMatch = false
1467 // Check various wildcard permutations if the domain name and the certificate Common Name are not empty.
1468 if ((domainName != null) && (certificateCommonName != null)) {
1469 // Check if the domains match.
1470 if (domainName == certificateCommonName)
1471 domainNamesMatch = true
1473 // If the domain name starts with a wildcard, check the base domain against all the subdomains of the certificate Common Name.
1474 if (!domainNamesMatch && domainName.startsWith("*.") && domainName.length > 2) {
1475 // Remove the initial `*.`.
1476 val baseDomainName = domainName.substring(2)
1478 // Create a copy of the certificate Common Name to test subdomains.
1479 var certificateCommonNameSubdomain: String = certificateCommonName
1481 // Check all the subdomains in the certificate Common Name subdomain against the base domain name.
1482 while (!domainNamesMatch && certificateCommonNameSubdomain.contains(".")) { // Stop checking if the domain names match or if there are no more dots.
1483 // Test the certificate Common Name subdomain against the base domain name.
1484 if (certificateCommonNameSubdomain == baseDomainName)
1485 domainNamesMatch = true
1487 // Strip out the lowest subdomain of the certificate Common Name subdomain.
1488 certificateCommonNameSubdomain = try {
1489 certificateCommonNameSubdomain.substring(certificateCommonNameSubdomain.indexOf(".") + 1)
1490 } catch (e: IndexOutOfBoundsException) { // The certificate Common Name subdomain ends with a dot.
1496 // If the certificate Common Name starts with a wildcard, check the base common name against all the subdomains of the domain name.
1497 if (!domainNamesMatch && certificateCommonName.startsWith("*.") && certificateCommonName.length > 2) {
1498 // Remove the initial `*.`.
1499 val baseCertificateCommonName = certificateCommonName.substring(2)
1501 // Setup a copy of domain name to test subdomains.
1502 var domainNameSubdomain: String = domainName
1504 // Check all the subdomains in the domain name subdomain against the base certificate Common Name.
1505 while (!domainNamesMatch && domainNameSubdomain.contains(".") && domainNameSubdomain.length > 2) {
1506 // Test the domain name subdomain against the base certificate Common Name.
1507 if (domainNameSubdomain == baseCertificateCommonName)
1508 domainNamesMatch = true
1510 // Strip out the lowest subdomain of the domain name subdomain.
1511 domainNameSubdomain = try {
1512 domainNameSubdomain.substring(domainNameSubdomain.indexOf(".") + 1)
1513 } catch (e: IndexOutOfBoundsException) { // `domainNameSubdomain` ends with a dot.
1519 // If both names start with a wildcard, check if the root of one contains the root of the other.
1520 if (!domainNamesMatch && domainName.startsWith("*.") && domainName.length > 2 && certificateCommonName.startsWith("*.") && certificateCommonName.length > 2) {
1521 // Remove the wildcards.
1522 val rootDomainName = domainName.substring(2)
1523 val rootCertificateCommonName = certificateCommonName.substring(2)
1525 // Check if one name ends with the contents of the other. If so, there will be overlap in the their wildcard subdomains.
1526 if (rootDomainName.endsWith(rootCertificateCommonName) || rootCertificateCommonName.endsWith(rootDomainName))
1527 domainNamesMatch = true
1531 return domainNamesMatch