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.ContextCompat.getColor
49 import androidx.core.content.res.ResourcesCompat
50 import androidx.fragment.app.Fragment
51 import androidx.preference.PreferenceManager
53 import com.stoutner.privacybrowser.R
54 import com.stoutner.privacybrowser.activities.DOMAINS_CUSTOM_USER_AGENT
55 import com.stoutner.privacybrowser.activities.DOMAINS_SYSTEM_DEFAULT_USER_AGENT
56 import com.stoutner.privacybrowser.activities.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT
57 import com.stoutner.privacybrowser.activities.SETTINGS_CUSTOM_USER_AGENT
58 import com.stoutner.privacybrowser.activities.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT
59 import com.stoutner.privacybrowser.activities.UNRECOGNIZED_USER_AGENT
60 import com.stoutner.privacybrowser.activities.DomainsActivity
61 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper
63 import java.lang.IndexOutOfBoundsException
64 import java.text.DateFormat
65 import java.util.Calendar
68 class DomainSettingsFragment : Fragment() {
69 // Define the class variables.
70 private var scrollY = 0
73 // Define the public constants.
74 const val DATABASE_ID = "database_id"
75 const val SCROLL_Y = "scroll_y"
77 // Define the public variables. `databaseId` is public so it can be accessed from `DomainsActivity`.
81 override fun onCreate(savedInstanceState: Bundle?) {
82 // Run the default commands.
83 super.onCreate(savedInstanceState)
85 // Store the arguments in class variables.
86 databaseId = requireArguments().getInt(DATABASE_ID)
87 scrollY = requireArguments().getInt(SCROLL_Y)
90 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
91 // Inflate the layout. The fragment will take care of attaching the root automatically.
92 val domainSettingsView = inflater.inflate(R.layout.domain_settings_fragment, container, false)
94 // Get the current theme status.
95 val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
97 // Get a handle for the shared preference.
98 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
100 // Store the default settings.
101 val defaultUserAgentName = sharedPreferences.getString(getString(R.string.user_agent_key), getString(R.string.user_agent_default_value))
102 val defaultCustomUserAgentString = sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value))
103 val defaultFontSizeString = sharedPreferences.getString(getString(R.string.font_size_key), getString(R.string.font_size_default_value))
104 val defaultSwipeToRefresh = sharedPreferences.getBoolean(getString(R.string.swipe_to_refresh_key), true)
105 val defaultWebViewTheme = sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value))
106 val defaultWideViewport = sharedPreferences.getBoolean(getString(R.string.wide_viewport_key), true)
107 val defaultDisplayWebpageImages = sharedPreferences.getBoolean(getString(R.string.display_webpage_images_key), true)
109 // Get handles for the views.
110 val domainSettingsScrollView = domainSettingsView.findViewById<ScrollView>(R.id.domain_settings_scrollview)
111 val domainNameEditText = domainSettingsView.findViewById<EditText>(R.id.domain_settings_name_edittext)
112 val javaScriptImageView = domainSettingsView.findViewById<ImageView>(R.id.javascript_imageview)
113 val javaScriptSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.javascript_switch)
114 val cookiesImageView = domainSettingsView.findViewById<ImageView>(R.id.cookies_imageview)
115 val cookiesSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.cookies_switch)
116 val domStorageImageView = domainSettingsView.findViewById<ImageView>(R.id.dom_storage_imageview)
117 val domStorageSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.dom_storage_switch)
118 val formDataImageView = domainSettingsView.findViewById<ImageView>(R.id.form_data_imageview) // The form data views can be remove once the minimum API >= 26.
119 val formDataSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.form_data_switch) // The form data views can be remove once the minimum API >= 26.
120 val easyListImageView = domainSettingsView.findViewById<ImageView>(R.id.easylist_imageview)
121 val easyListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.easylist_switch)
122 val easyPrivacyImageView = domainSettingsView.findViewById<ImageView>(R.id.easyprivacy_imageview)
123 val easyPrivacySwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.easyprivacy_switch)
124 val fanboysAnnoyanceListImageView = domainSettingsView.findViewById<ImageView>(R.id.fanboys_annoyance_list_imageview)
125 val fanboysAnnoyanceListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.fanboys_annoyance_list_switch)
126 val fanboysSocialBlockingListImageView = domainSettingsView.findViewById<ImageView>(R.id.fanboys_social_blocking_list_imageview)
127 val fanboysSocialBlockingListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.fanboys_social_blocking_list_switch)
128 val ultraListImageView = domainSettingsView.findViewById<ImageView>(R.id.ultralist_imageview)
129 val ultraListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.ultralist_switch)
130 val ultraPrivacyImageView = domainSettingsView.findViewById<ImageView>(R.id.ultraprivacy_imageview)
131 val ultraPrivacySwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.ultraprivacy_switch)
132 val blockAllThirdPartyRequestsImageView = domainSettingsView.findViewById<ImageView>(R.id.block_all_third_party_requests_imageview)
133 val blockAllThirdPartyRequestsSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.block_all_third_party_requests_switch)
134 val userAgentLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.user_agent_linear_layout)
135 val userAgentSpinner = domainSettingsView.findViewById<Spinner>(R.id.user_agent_spinner)
136 val userAgentTextView = domainSettingsView.findViewById<TextView>(R.id.user_agent_textview)
137 val customUserAgentEditText = domainSettingsView.findViewById<EditText>(R.id.custom_user_agent_edittext)
138 val fontSizeSpinner = domainSettingsView.findViewById<Spinner>(R.id.font_size_spinner)
139 val defaultFontSizeTextView = domainSettingsView.findViewById<TextView>(R.id.default_font_size_textview)
140 val customFontSizeEditText = domainSettingsView.findViewById<EditText>(R.id.custom_font_size_edittext)
141 val swipeToRefreshImageView = domainSettingsView.findViewById<ImageView>(R.id.swipe_to_refresh_imageview)
142 val swipeToRefreshSpinner = domainSettingsView.findViewById<Spinner>(R.id.swipe_to_refresh_spinner)
143 val swipeToRefreshTextView = domainSettingsView.findViewById<TextView>(R.id.swipe_to_refresh_textview)
144 val webViewThemeLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.webview_theme_linearlayout)
145 val webViewThemeImageView = domainSettingsView.findViewById<ImageView>(R.id.webview_theme_imageview)
146 val webViewThemeSpinner = domainSettingsView.findViewById<Spinner>(R.id.webview_theme_spinner)
147 val webViewThemeTextView = domainSettingsView.findViewById<TextView>(R.id.webview_theme_textview)
148 val wideViewportImageView = domainSettingsView.findViewById<ImageView>(R.id.wide_viewport_imageview)
149 val wideViewportSpinner = domainSettingsView.findViewById<Spinner>(R.id.wide_viewport_spinner)
150 val wideViewportTextView = domainSettingsView.findViewById<TextView>(R.id.wide_viewport_textview)
151 val displayWebpageImagesImageView = domainSettingsView.findViewById<ImageView>(R.id.display_webpage_images_imageview)
152 val displayWebpageImagesSpinner = domainSettingsView.findViewById<Spinner>(R.id.display_webpage_images_spinner)
153 val displayImagesTextView = domainSettingsView.findViewById<TextView>(R.id.display_webpage_images_textview)
154 val pinnedSslCertificateImageView = domainSettingsView.findViewById<ImageView>(R.id.pinned_ssl_certificate_imageview)
155 val pinnedSslCertificateSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.pinned_ssl_certificate_switch)
156 val savedSslCardView = domainSettingsView.findViewById<CardView>(R.id.saved_ssl_certificate_cardview)
157 val savedSslCertificateLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.saved_ssl_certificate_linearlayout)
158 val savedSslCertificateRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.saved_ssl_certificate_radiobutton)
159 val savedSslIssuedToCNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_cname)
160 val savedSslIssuedToONameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_oname)
161 val savedSslIssuedToUNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_uname)
162 val savedSslIssuedByCNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_cname)
163 val savedSslIssuedByONameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_oname)
164 val savedSslIssuedByUNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_uname)
165 val savedSslStartDateTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_start_date)
166 val savedSslEndDateTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_end_date)
167 val currentSslCardView = domainSettingsView.findViewById<CardView>(R.id.current_website_certificate_cardview)
168 val currentWebsiteCertificateLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.current_website_certificate_linearlayout)
169 val currentWebsiteCertificateRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.current_website_certificate_radiobutton)
170 val currentSslIssuedToCNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_cname)
171 val currentSslIssuedToONameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_oname)
172 val currentSslIssuedToUNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_uname)
173 val currentSslIssuedByCNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_cname)
174 val currentSslIssuedByONameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_oname)
175 val currentSslIssuedByUNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_uname)
176 val currentSslStartDateTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_start_date)
177 val currentSslEndDateTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_end_date)
178 val noCurrentWebsiteCertificateTextView = domainSettingsView.findViewById<TextView>(R.id.no_current_website_certificate)
179 val pinnedIpAddressesImageView = domainSettingsView.findViewById<ImageView>(R.id.pinned_ip_addresses_imageview)
180 val pinnedIpAddressesSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.pinned_ip_addresses_switch)
181 val savedIpAddressesCardView = domainSettingsView.findViewById<CardView>(R.id.saved_ip_addresses_cardview)
182 val savedIpAddressesLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.saved_ip_addresses_linearlayout)
183 val savedIpAddressesRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.saved_ip_addresses_radiobutton)
184 val savedIpAddressesTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ip_addresses_textview)
185 val currentIpAddressesCardView = domainSettingsView.findViewById<CardView>(R.id.current_ip_addresses_cardview)
186 val currentIpAddressesLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.current_ip_addresses_linearlayout)
187 val currentIpAddressesRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.current_ip_addresses_radiobutton)
188 val currentIpAddressesTextView = domainSettingsView.findViewById<TextView>(R.id.current_ip_addresses_textview)
190 // Hide the WebView theme linear layout if the API < 29.
191 if (Build.VERSION.SDK_INT < 29)
192 webViewThemeLinearLayout.visibility = View.GONE
194 // Setup the pinned labels.
195 val cNameLabel = getString(R.string.common_name)
196 val oNameLabel = getString(R.string.organization)
197 val uNameLabel = getString(R.string.organizational_unit)
198 val startDateLabel = getString(R.string.start_date)
199 val endDateLabel = getString(R.string.end_date)
201 // Initialize the database handler.
202 val domainsDatabaseHelper = DomainsDatabaseHelper(requireContext())
204 // Get the database cursor for this ID.
205 val domainCursor = domainsDatabaseHelper.getCursorForId(databaseId)
207 // Move to the first row.
208 domainCursor.moveToFirst()
210 // Save the cursor entries as variables.
211 val domainNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME))
212 val javaScriptInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT))
213 val cookiesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES))
214 val domStorageInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE))
215 val formDataInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA)) // Form data can be remove once the minimum API >= 26.
216 val easyListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST))
217 val easyPrivacyInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY))
218 val fanboysAnnoyanceListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST))
219 val fanboysSocialBlockingListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST))
220 val ultraListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST))
221 val ultraPrivacyInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY))
222 val blockAllThirdPartyRequestsInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS))
223 val currentUserAgentName = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT))
224 val fontSizeInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE))
225 val swipeToRefreshInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH))
226 val webViewThemeInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME))
227 val wideViewportInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WIDE_VIEWPORT))
228 val displayImagesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DISPLAY_IMAGES))
229 val pinnedSslCertificateInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE))
230 val savedSslIssuedToCNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME))
231 val savedSslIssuedToONameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION))
232 val savedSslIssuedToUNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT))
233 val savedSslIssuedByCNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME))
234 val savedSslIssuedByONameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION))
235 val savedSslIssuedByUNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT))
236 val pinnedIpAddressesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_IP_ADDRESSES))
237 val savedIpAddresses = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.IP_ADDRESSES))
239 // Get the SSL dates from the database.
240 val savedSslStartDateLong = domainCursor.getLong(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_START_DATE))
241 val savedSslEndDateLong = domainCursor.getLong(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_END_DATE))
243 // Initialize the saved SSL certificate date variables.
244 var savedSslStartDate: Date? = null
245 var savedSslEndDate: Date? = null
247 // Only get the saved SSL certificate dates from the cursor if they are not set to `0`.
248 if (savedSslStartDateLong != 0L)
249 savedSslStartDate = Date(savedSslStartDateLong)
250 if (savedSslEndDateLong != 0L)
251 savedSslEndDate = Date(savedSslEndDateLong)
253 // Create array adapters for the spinners.
254 val translatedUserAgentArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.translated_domain_settings_user_agent_names, R.layout.spinner_item)
255 val fontSizeArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.font_size_array, R.layout.spinner_item)
256 val swipeToRefreshArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.swipe_to_refresh_array, R.layout.spinner_item)
257 val webViewThemeArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.webview_theme_array, R.layout.spinner_item)
258 val wideViewportArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.wide_viewport_array, R.layout.spinner_item)
259 val displayImagesArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.display_webpage_images_array, R.layout.spinner_item)
261 // Set the drop down view resource on the spinners.
262 translatedUserAgentArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
263 fontSizeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
264 swipeToRefreshArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
265 webViewThemeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
266 wideViewportArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
267 displayImagesArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
269 // Set the array adapters for the spinners.
270 userAgentSpinner.adapter = translatedUserAgentArrayAdapter
271 fontSizeSpinner.adapter = fontSizeArrayAdapter
272 swipeToRefreshSpinner.adapter = swipeToRefreshArrayAdapter
273 webViewThemeSpinner.adapter = webViewThemeArrayAdapter
274 wideViewportSpinner.adapter = wideViewportArrayAdapter
275 displayWebpageImagesSpinner.adapter = displayImagesArrayAdapter
277 // Create a spannable string builder for each TextView that needs multiple colors of text.
278 val savedSslIssuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString)
279 val savedSslIssuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + savedSslIssuedToONameString)
280 val savedSslIssuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + savedSslIssuedToUNameString)
281 val savedSslIssuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedByCNameString)
282 val savedSslIssuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + savedSslIssuedByONameString)
283 val savedSslIssuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + savedSslIssuedByUNameString)
285 // Create the date spannable string builders.
286 val savedSslStartDateStringBuilder: SpannableStringBuilder = if (savedSslStartDate == null)
287 SpannableStringBuilder(startDateLabel)
289 SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslStartDate))
291 val savedSslEndDateStringBuilder: SpannableStringBuilder = if (savedSslEndDate == null)
292 SpannableStringBuilder(endDateLabel)
294 SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslEndDate))
296 // Create the color spans.
297 val blueColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.alt_blue_text))
298 val redColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.red_text))
300 // Set the domain name from the the database cursor.
301 domainNameEditText.setText(domainNameString)
303 // Update the certificates' Common Name color when the domain name text changes.
304 domainNameEditText.addTextChangedListener(object : TextWatcher {
305 override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
309 override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
313 override fun afterTextChanged(s: Editable) {
314 // Get the new domain name.
315 val newDomainName = domainNameEditText.text.toString()
317 // Check the saved SSL certificate against the new domain name.
318 val savedSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, savedSslIssuedToCNameString)
320 // Create a spannable string builder for the saved certificate's Common Name.
321 val savedSslCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString)
323 // Format the saved certificate's Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
324 if (savedSslMatchesNewDomainName) {
325 savedSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
327 savedSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, savedSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
330 // Update the saved SSL issued to CName text view.
331 savedSslIssuedToCNameTextView.text = savedSslCNameStringBuilder
333 // Update the current website certificate if it exists.
334 if (DomainsActivity.sslIssuedToCName != null) {
335 // Check the current website certificate against the new domain name.
336 val currentSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, DomainsActivity.sslIssuedToCName)
338 // Create a spannable string builder for the current website certificate's Common Name.
339 val currentSslCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName)
341 // Format the current certificate Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
342 if (currentSslMatchesNewDomainName) {
343 currentSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
345 currentSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, currentSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
348 // Update the current SSL issued to CName text view.
349 currentSslIssuedToCNameTextView.text = currentSslCNameStringBuilder
354 // Set the switch positions.
355 javaScriptSwitch.isChecked = (javaScriptInt == 1)
356 cookiesSwitch.isChecked = (cookiesInt == 1)
357 domStorageSwitch.isChecked = (domStorageInt == 1)
358 formDataSwitch.isChecked = (formDataInt == 1) // Form data can be removed once the minimum API >= 26.
359 easyListSwitch.isChecked = (easyListInt == 1)
360 easyPrivacySwitch.isChecked = (easyPrivacyInt == 1)
361 fanboysAnnoyanceListSwitch.isChecked = (fanboysAnnoyanceListInt == 1)
362 fanboysSocialBlockingListSwitch.isChecked = (fanboysSocialBlockingListInt == 1)
363 ultraListSwitch.isChecked = (ultraListInt == 1)
364 ultraPrivacySwitch.isChecked = (ultraPrivacyInt == 1)
365 blockAllThirdPartyRequestsSwitch.isChecked = (blockAllThirdPartyRequestsInt == 1)
366 pinnedSslCertificateSwitch.isChecked = (pinnedSslCertificateInt == 1)
367 pinnedIpAddressesSwitch.isChecked = (pinnedIpAddressesInt == 1)
369 // Set the switch icon colors.
370 cookiesImageView.isSelected = (cookiesInt == 1)
371 domStorageImageView.isSelected = (domStorageInt == 1)
372 formDataImageView.isSelected = (formDataInt == 1) // Form data can be removed once the minimum API >= 26.
373 easyListImageView.isSelected = (easyListInt == 1)
374 easyPrivacyImageView.isSelected = (easyPrivacyInt == 1)
375 fanboysAnnoyanceListImageView.isSelected = (fanboysAnnoyanceListInt == 1)
376 fanboysSocialBlockingListImageView.isSelected = (fanboysSocialBlockingListInt == 1)
377 ultraListImageView.isSelected = (ultraListInt == 1)
378 ultraPrivacyImageView.isSelected = (ultraPrivacyInt == 1)
379 blockAllThirdPartyRequestsImageView.isSelected = (blockAllThirdPartyRequestsInt == 1)
380 pinnedSslCertificateImageView.isSelected = (pinnedSslCertificateInt == 1)
381 pinnedIpAddressesImageView.isSelected = (pinnedIpAddressesInt == 1)
383 // Set the JavaScript icon.
384 if (javaScriptInt == 1)
385 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null))
387 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null))
389 // Set the DOM storage switch status based on the JavaScript status.
390 domStorageSwitch.isEnabled = (javaScriptInt == 1)
392 // Set the DOM storage icon ghosted status based on the JavaScript status.
393 domStorageImageView.isEnabled = (javaScriptInt == 1)
395 // Set the form data visibility. Form data can be removed once the minimum API >= 26.
396 if (Build.VERSION.SDK_INT >= 26) {
397 // Hide the form data image view and switch.
398 formDataImageView.visibility = View.GONE
399 formDataSwitch.visibility = View.GONE
402 // Set Fanboy's Social Blocking List switch status based on the Annoyance List status.
403 fanboysSocialBlockingListSwitch.isEnabled = (fanboysAnnoyanceListInt == 0)
405 // Set the Social Blocking List icon ghosted status based on the Annoyance List status.
406 fanboysSocialBlockingListImageView.isEnabled = (fanboysAnnoyanceListInt == 0)
408 // Open the spinners when the text view is clicked.
409 userAgentTextView.setOnClickListener { userAgentSpinner.performClick() }
410 defaultFontSizeTextView.setOnClickListener { fontSizeSpinner.performClick() }
411 swipeToRefreshTextView.setOnClickListener { swipeToRefreshSpinner.performClick() }
412 webViewThemeTextView.setOnClickListener { webViewThemeSpinner.performClick() }
413 wideViewportTextView.setOnClickListener { wideViewportSpinner.performClick() }
414 displayImagesTextView.setOnClickListener { displayWebpageImagesSpinner.performClick() }
416 // Inflated a WebView to get the default user agent.
417 // `@SuppressLint("InflateParams")` removes the warning about using `null` as the `ViewGroup`, which in this case makes sense because the bare WebView should not be displayed on the screen.
418 @SuppressLint("InflateParams") val bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false)
419 val bareWebView = bareWebViewLayout.findViewById<WebView>(R.id.bare_webview)
420 val webViewDefaultUserAgentString = bareWebView.settings.userAgentString
422 // Get a handle for the user agent array adapter. This array does not contain the `System default` entry.
423 val userAgentNamesArray = ArrayAdapter.createFromResource(requireContext(), R.array.user_agent_names, R.layout.spinner_item)
425 // Get the positions of the user agent and the default user agent.
426 val userAgentArrayPosition = userAgentNamesArray.getPosition(currentUserAgentName)
427 val defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName)
429 // Get a handle for the user agent data array. This array does not contain the `System default` entry.
430 val userAgentDataArray = resources.getStringArray(R.array.user_agent_data)
432 // Set the user agent text.
433 if (currentUserAgentName == getString(R.string.system_default_user_agent)) { // Use the system default user agent.
434 // Set the user agent according to the system default.
435 when (defaultUserAgentArrayPosition) {
436 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
437 UNRECOGNIZED_USER_AGENT -> userAgentTextView.text = defaultUserAgentName
439 // Display the WebView default user agent.
440 SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentTextView.text = webViewDefaultUserAgentString
442 // Display the custom user agent.
443 SETTINGS_CUSTOM_USER_AGENT -> userAgentTextView.text = defaultCustomUserAgentString
445 // Get the user agent string from the user agent data array.
446 else -> userAgentTextView.text = userAgentDataArray[defaultUserAgentArrayPosition]
449 // Set the background color to be transparent.
450 userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
451 } else if (userAgentArrayPosition == UNRECOGNIZED_USER_AGENT || currentUserAgentName == getString(R.string.custom_user_agent)) {
452 // A custom user agent is stored in the current user agent name. The second check is necessary in case the user did not change the default custom text.
453 // Set the user agent spinner to `Custom user agent`.
454 userAgentSpinner.setSelection(DOMAINS_CUSTOM_USER_AGENT)
456 // Hide the user agent text view.
457 userAgentTextView.visibility = View.GONE
459 // Show the custom user agent edit text and set the current user agent name as the text.
460 customUserAgentEditText.visibility = View.VISIBLE
461 customUserAgentEditText.setText(currentUserAgentName)
463 // Set the background color to be blue.
464 userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
465 } else { // The user agent name contains one of the canonical user agents.
466 // Set the user agent spinner selection. The spinner has one more entry at the beginning than the user agent data array, so the position must be incremented.
467 userAgentSpinner.setSelection(userAgentArrayPosition + 1)
469 // Show the user agent text view.
470 userAgentTextView.visibility = View.VISIBLE
472 // Hide the custom user agent edit text.
473 customUserAgentEditText.visibility = View.GONE
475 // Set the user agent text.
476 if (userAgentArrayPosition == DOMAINS_WEBVIEW_DEFAULT_USER_AGENT) { // The WebView default user agent is selected.
477 // Display the WebView default user agent.
478 userAgentTextView.text = webViewDefaultUserAgentString
479 } else { // A user agent besides the default is selected.
480 // Get the user agent string from the user agent data array. The spinner has one more entry at the beginning than the user agent data array, so the position must be incremented.
481 userAgentTextView.text = userAgentDataArray[userAgentArrayPosition + 1]
484 // Set the background color to be blue.
485 userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
489 // Display the font size settings.
490 if (fontSizeInt == 0) { // `0` is the code for system default font size.
491 // Set the font size to the system default.
492 fontSizeSpinner.setSelection(0)
494 // Show the default font size text view.
495 defaultFontSizeTextView.visibility = View.VISIBLE
497 // Hide the custom font size edit text.
498 customFontSizeEditText.visibility = View.GONE
500 // Set the default font size as the text of the custom font size edit text. This way, if the user switches to custom it will already be populated.
501 customFontSizeEditText.setText(defaultFontSizeString)
502 } else { // A custom font size is selected.
503 // Set the spinner to the custom font size.
504 fontSizeSpinner.setSelection(1)
506 // Hide the default font size text view.
507 defaultFontSizeTextView.visibility = View.GONE
509 // Show the custom font size edit text.
510 customFontSizeEditText.visibility = View.GONE
512 // Set the custom font size.
513 customFontSizeEditText.setText(fontSizeInt.toString())
516 // Initialize the default font size percentage string.
517 val defaultFontSizePercentageString = "$defaultFontSizeString%"
519 // Set the default font size text in the text view.
520 defaultFontSizeTextView.text = defaultFontSizePercentageString
522 // Select the swipe-to-refresh selection in the spinner.
523 swipeToRefreshSpinner.setSelection(swipeToRefreshInt)
525 // Set the swipe-to-refresh text.
526 if (defaultSwipeToRefresh)
527 swipeToRefreshTextView.text = swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
529 swipeToRefreshTextView.text = swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
531 // Set the swipe-to-refresh icon and text view settings.
532 when (swipeToRefreshInt) {
533 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
534 // Set the icon color.
535 swipeToRefreshImageView.isSelected = defaultSwipeToRefresh
537 // Show the swipe-to-refresh text view.
538 swipeToRefreshTextView.visibility = View.VISIBLE
541 DomainsDatabaseHelper.ENABLED -> {
542 // Set the icon color.
543 swipeToRefreshImageView.isSelected = true
545 // Hide the swipe-to-refresh text view.
546 swipeToRefreshTextView.visibility = View.GONE
549 DomainsDatabaseHelper.DISABLED -> {
550 // Set the icon color.
551 swipeToRefreshImageView.isSelected = false
553 // Hide the swipe-to-refresh text view.
554 swipeToRefreshTextView.visibility = View.GONE
558 // Get the WebView theme string arrays.
559 val webViewThemeStringArray = resources.getStringArray(R.array.webview_theme_array)
560 val webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values)
562 // Get the WebView theme entry number that matches the current WebView theme.
563 val appWebViewThemeEntryNumber = when (defaultWebViewTheme) {
564 webViewThemeEntryValuesStringArray[1] -> { 1 } // The light theme is selected.
565 webViewThemeEntryValuesStringArray[2] -> { 2 } // The dark theme is selected.
566 else -> { 0 } // The system default theme is selected.
569 // Select the WebView theme in the spinner.
570 webViewThemeSpinner.setSelection(webViewThemeInt)
572 // Set the WebView theme text.
573 if (appWebViewThemeEntryNumber == DomainsDatabaseHelper.SYSTEM_DEFAULT) { // The app WebView theme is system default.
574 // Set the text according to the current UI theme.
575 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
576 webViewThemeTextView.text = webViewThemeStringArray[DomainsDatabaseHelper.LIGHT_THEME]
578 webViewThemeTextView.text = webViewThemeStringArray[DomainsDatabaseHelper.DARK_THEME]
579 } else { // The app WebView theme is not system default.
580 // Set the text according to the app WebView theme.
581 webViewThemeTextView.text = webViewThemeStringArray[appWebViewThemeEntryNumber]
584 // Set the WebView theme icon and text visibility.
585 when (webViewThemeInt) {
586 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
587 // Set the icon color.
588 when (appWebViewThemeEntryNumber) {
589 DomainsDatabaseHelper.SYSTEM_DEFAULT -> webViewThemeImageView.isSelected = (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
590 DomainsDatabaseHelper.LIGHT_THEME -> webViewThemeImageView.isSelected = true
591 DomainsDatabaseHelper.DARK_THEME -> webViewThemeImageView.isSelected = false
594 // Show the WebView theme text view.
595 webViewThemeTextView.visibility = View.VISIBLE
598 DomainsDatabaseHelper.LIGHT_THEME -> {
599 // Set the icon color.
600 webViewThemeImageView.isSelected = true
602 // Hide the WebView theme text view.
603 webViewThemeTextView.visibility = View.GONE
606 DomainsDatabaseHelper.DARK_THEME -> {
607 // Set the icon color.
608 webViewThemeImageView.isSelected = false
610 // Hide the WebView theme text view.
611 webViewThemeTextView.visibility = View.GONE
615 // Select the wide viewport in the spinner.
616 wideViewportSpinner.setSelection(wideViewportInt)
618 // Set the default wide viewport text.
619 if (defaultWideViewport)
620 wideViewportTextView.text = wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
621 else wideViewportTextView.text = wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
623 // Set the wide viewport icon and text view settings.
624 when (wideViewportInt) {
625 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
626 // Set the icon color.
627 wideViewportImageView.isSelected = defaultWideViewport
629 // Show the wide viewport text view.
630 wideViewportTextView.visibility = View.VISIBLE
633 DomainsDatabaseHelper.ENABLED -> {
634 // Set the icon color.
635 wideViewportImageView.isSelected = true
637 // Hide the wide viewport text view.
638 wideViewportTextView.visibility = View.GONE
641 DomainsDatabaseHelper.DISABLED -> {
642 // Set the icon color.
643 wideViewportImageView.isSelected = false
645 // Hide the wide viewport text view.
646 wideViewportTextView.visibility = View.GONE
650 // Display the website images mode in the spinner.
651 displayWebpageImagesSpinner.setSelection(displayImagesInt)
653 // Set the default display images text.
654 if (defaultDisplayWebpageImages)
655 displayImagesTextView.text = displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
657 displayImagesTextView.text = displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
659 // Set the display website images icon and text view settings.
660 when (displayImagesInt) {
661 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
662 // Set the icon color.
663 displayWebpageImagesImageView.isSelected = defaultDisplayWebpageImages
665 // Show the display images text view.
666 displayImagesTextView.visibility = View.VISIBLE
669 DomainsDatabaseHelper.ENABLED -> {
670 // Set the icon color.
671 displayWebpageImagesImageView.isSelected = true
673 // Hide the display images text view.
674 displayImagesTextView.visibility = View.GONE
677 DomainsDatabaseHelper.DISABLED -> {
678 // Set the icon color.
679 displayWebpageImagesImageView.isSelected = false
681 // Hide the display images text view.
682 displayImagesTextView.visibility = View.GONE
686 // Store the current date.
687 val currentDate = Calendar.getInstance().time
689 // Setup the string builders to display the general certificate information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
690 savedSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, savedSslIssuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
691 savedSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, savedSslIssuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
692 savedSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslIssuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
693 savedSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, savedSslIssuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
694 savedSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, savedSslIssuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
696 // Check the certificate Common Name against the domain name.
697 val savedSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslIssuedToCNameString)
699 // Format the issued to Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
700 if (savedSslCommonNameMatchesDomainName)
701 savedSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
703 savedSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, savedSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
705 // Format the start date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
706 if (savedSslStartDate != null && savedSslStartDate.after(currentDate)) // The certificate start date is in the future.
707 savedSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, savedSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
708 else // The certificate start date is in the past.
709 savedSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, savedSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
711 // Format the end date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
712 if (savedSslEndDate != null && savedSslEndDate.before(currentDate)) // The certificate end date is in the past.
713 savedSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, savedSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
714 else // The certificate end date is in the future.
715 savedSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, savedSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
717 // Display the saved website SSL certificate strings.
718 savedSslIssuedToCNameTextView.text = savedSslIssuedToCNameStringBuilder
719 savedSslIssuedToONameTextView.text = savedSslIssuedToONameStringBuilder
720 savedSslIssuedToUNameTextView.text = savedSslIssuedToUNameStringBuilder
721 savedSslIssuedByCNameTextView.text = savedSslIssuedByCNameStringBuilder
722 savedSslIssuedByONameTextView.text = savedSslIssuedByONameStringBuilder
723 savedSslIssuedByUNameTextView.text = savedSslIssuedByUNameStringBuilder
724 savedSslStartDateTextView.text = savedSslStartDateStringBuilder
725 savedSslEndDateTextView.text = savedSslEndDateStringBuilder
727 // Populate the current website SSL certificate if there is one.
728 if (DomainsActivity.sslIssuedToCName != null) {
729 // Get dates from the raw long values.
730 val currentSslStartDate = Date(DomainsActivity.sslStartDateLong)
731 val currentSslEndDate = Date(DomainsActivity.sslEndDateLong)
733 // Create a spannable string builder for each text view that needs multiple colors of text.
734 val currentSslIssuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName)
735 val currentSslIssuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedToOName)
736 val currentSslIssuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedToUName)
737 val currentSslIssuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedByCName)
738 val currentSslIssuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedByOName)
739 val currentSslIssuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedByUName)
740 val currentSslStartDateStringBuilder = SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslStartDate))
741 val currentSslEndDateStringBuilder = SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslEndDate))
743 // Setup the string builders to display the general certificate information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
744 currentSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, currentSslIssuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
745 currentSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, currentSslIssuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
746 currentSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslIssuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
747 currentSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, currentSslIssuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
748 currentSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, currentSslIssuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
750 // Check the certificate Common Name against the domain name.
751 val currentSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, DomainsActivity.sslIssuedToCName)
753 // Format the issued to Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
754 if (currentSslCommonNameMatchesDomainName)
755 currentSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
757 currentSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, currentSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
759 // Format the start date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
760 if (currentSslStartDate.after(currentDate)) // The certificate start date is in the future.
761 currentSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, currentSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
762 else // The certificate start date is in the past.
763 currentSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, currentSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
765 // Format the end date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
766 if (currentSslEndDate.before(currentDate)) // The certificate end date is in the past.
767 currentSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, currentSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
768 else // The certificate end date is in the future.
769 currentSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, currentSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
771 // Display the current website SSL certificate strings.
772 currentSslIssuedToCNameTextView.text = currentSslIssuedToCNameStringBuilder
773 currentSslIssuedToONameTextView.text = currentSslIssuedToONameStringBuilder
774 currentSslIssuedToUNameTextView.text = currentSslIssuedToUNameStringBuilder
775 currentSslIssuedByCNameTextView.text = currentSslIssuedByCNameStringBuilder
776 currentSslIssuedByONameTextView.text = currentSslIssuedByONameStringBuilder
777 currentSslIssuedByUNameTextView.text = currentSslIssuedByUNameStringBuilder
778 currentSslStartDateTextView.text = currentSslStartDateStringBuilder
779 currentSslEndDateTextView.text = currentSslEndDateStringBuilder
782 // Set the initial display status of the SSL certificates card views.
783 if (pinnedSslCertificateSwitch.isChecked) { // An SSL certificate is pinned.
784 // Set the visibility of the saved SSL certificate.
785 if (savedSslIssuedToCNameString == null)
786 savedSslCardView.visibility = View.GONE
788 savedSslCardView.visibility = View.VISIBLE
790 // Set the visibility of the current website SSL certificate.
791 if (DomainsActivity.sslIssuedToCName == null) { // There is no current SSL certificate.
792 // Hide the SSL certificate.
793 currentSslCardView.visibility = View.GONE
795 // Show the instruction.
796 noCurrentWebsiteCertificateTextView.visibility = View.VISIBLE
797 } else { // There is a current SSL certificate.
798 // Show the SSL certificate.
799 currentSslCardView.visibility = View.VISIBLE
801 // Hide the instruction.
802 noCurrentWebsiteCertificateTextView.visibility = View.GONE
805 // Set the status of the radio buttons and the card view backgrounds.
806 if (savedSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is displayed.
807 // Check the saved SSL certificate radio button.
808 savedSslCertificateRadioButton.isChecked = true
810 // Uncheck the current website SSL certificate radio button.
811 currentWebsiteCertificateRadioButton.isChecked = false
813 // Darken the background of the current website SSL certificate linear layout.
814 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
815 } else if (currentSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is hidden but the current website SSL certificate is visible.
816 // Check the current website SSL certificate radio button.
817 currentWebsiteCertificateRadioButton.isChecked = true
819 // Uncheck the saved SSL certificate radio button.
820 savedSslCertificateRadioButton.isChecked = false
821 } else { // Neither SSL certificate is visible.
822 // Uncheck both radio buttons.
823 savedSslCertificateRadioButton.isChecked = false
824 currentWebsiteCertificateRadioButton.isChecked = false
826 } else { // An SSL certificate is not pinned.
827 // Hide the SSl certificates and instructions.
828 savedSslCardView.visibility = View.GONE
829 currentSslCardView.visibility = View.GONE
830 noCurrentWebsiteCertificateTextView.visibility = View.GONE
832 // Uncheck the radio buttons.
833 savedSslCertificateRadioButton.isChecked = false
834 currentWebsiteCertificateRadioButton.isChecked = false
837 // Populate the saved and current IP addresses.
838 savedIpAddressesTextView.text = savedIpAddresses
839 currentIpAddressesTextView.text = DomainsActivity.currentIpAddresses
841 // Set the initial display status of the IP addresses card views.
842 if (pinnedIpAddressesSwitch.isChecked) { // IP addresses are pinned.
843 // Set the visibility of the saved IP addresses.
844 if (savedIpAddresses == null) // There are no saved IP addresses.
845 savedIpAddressesCardView.visibility = View.GONE
846 else // There are saved IP addresses.
847 savedIpAddressesCardView.visibility = View.VISIBLE
849 // Set the visibility of the current IP addresses.
850 currentIpAddressesCardView.visibility = View.VISIBLE
852 // Set the status of the radio buttons and the card view backgrounds.
853 if (savedIpAddressesCardView.visibility == View.VISIBLE) { // The saved IP addresses are displayed.
854 // Check the saved IP addresses radio button.
855 savedIpAddressesRadioButton.isChecked = true
857 // Uncheck the current IP addresses radio button.
858 currentIpAddressesRadioButton.isChecked = false
860 // Darken the background of the current IP addresses linear layout.
861 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
862 } else { // The saved IP addresses are hidden.
863 // Check the current IP addresses radio button.
864 currentIpAddressesRadioButton.isChecked = true
866 // Uncheck the saved IP addresses radio button.
867 savedIpAddressesRadioButton.isChecked = false
869 } else { // IP addresses are not pinned.
870 // Hide the IP addresses card views.
871 savedIpAddressesCardView.visibility = View.GONE
872 currentIpAddressesCardView.visibility = View.GONE
874 // Uncheck the radio buttons.
875 savedIpAddressesRadioButton.isChecked = false
876 currentIpAddressesRadioButton.isChecked = false
880 // Set the JavaScript switch listener.
881 javaScriptSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
882 // Update the JavaScript icon.
884 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null))
886 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null))
888 // Set the DOM storage switch status.
889 domStorageSwitch.isEnabled = isChecked
891 // Set the DOM storage ghosted icon status.
892 domStorageImageView.isEnabled = isChecked
895 // Set the cookies switch listener.
896 cookiesSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
897 // Update the icon color.
898 cookiesImageView.isSelected = isChecked
901 // Set the DOM Storage switch listener.
902 domStorageSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
903 // Update the icon color.
904 domStorageImageView.isSelected = isChecked
907 // Set the form data switch listener. It can be removed once the minimum API >= 26.
908 if (Build.VERSION.SDK_INT < 26) {
909 formDataSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
910 // Update the icon color.
911 formDataImageView.isSelected = isChecked
915 // Set the EasyList switch listener.
916 easyListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
917 // Update the icon color.
918 easyListImageView.isSelected = isChecked
921 // Set the EasyPrivacy switch listener.
922 easyPrivacySwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
923 // Update the icon color.
924 easyPrivacyImageView.isSelected = isChecked
927 // Set the Fanboy's Annoyance List switch listener.
928 fanboysAnnoyanceListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
929 // Update the icon color.
930 fanboysAnnoyanceListImageView.isSelected = isChecked
932 // Set Fanboy's Social Blocking List switch position.
933 fanboysSocialBlockingListSwitch.isEnabled = !isChecked
935 // Set the Social Blocking List icon ghosted status.
936 fanboysSocialBlockingListImageView.isEnabled = !isChecked
939 // Set the Fanboy's Social Blocking List switch listener.
940 fanboysSocialBlockingListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
941 // Update the icon color.
942 fanboysSocialBlockingListImageView.isSelected = isChecked
945 // Set the UltraList switch listener.
946 ultraListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
947 // Update the icon color.
948 ultraListImageView.isSelected = isChecked
951 // Set the UltraPrivacy switch listener.
952 ultraPrivacySwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
953 // Update the icon color.
954 ultraPrivacyImageView.isSelected = isChecked
957 // Set the block all third-party requests switch listener.
958 blockAllThirdPartyRequestsSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
959 // Update the icon color.
960 blockAllThirdPartyRequestsImageView.isSelected = isChecked
963 // Set the user agent spinner listener.
964 userAgentSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
965 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
966 // Set the new user agent.
968 DOMAINS_SYSTEM_DEFAULT_USER_AGENT -> {
969 // Show the user agent text view.
970 userAgentTextView.visibility = View.VISIBLE
972 // Hide the custom user agent edit text.
973 customUserAgentEditText.visibility = View.GONE
975 // Set the user text.
976 when (defaultUserAgentArrayPosition) {
977 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
978 UNRECOGNIZED_USER_AGENT -> userAgentTextView.text = defaultUserAgentName
980 // Display the `WebView` default user agent.
981 SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentTextView.text = webViewDefaultUserAgentString
983 // Display the custom user agent.
984 SETTINGS_CUSTOM_USER_AGENT -> userAgentTextView.text = defaultCustomUserAgentString
986 // Get the user agent string from the user agent data array.
987 else -> userAgentTextView.text = userAgentDataArray[defaultUserAgentArrayPosition]
990 // Set the background color to be transparent.
991 userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
994 DOMAINS_WEBVIEW_DEFAULT_USER_AGENT -> {
995 // Show the user agent text view.
996 userAgentTextView.visibility = View.VISIBLE
998 // Set the user agent text.
999 userAgentTextView.text = webViewDefaultUserAgentString
1001 // Hide the custom user agent EditTex.
1002 customUserAgentEditText.visibility = View.GONE
1004 // Set the background color to be blue.
1005 userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1008 DOMAINS_CUSTOM_USER_AGENT -> {
1009 // Hide the user agent TextView.
1010 userAgentTextView.visibility = View.GONE
1012 // Show the custom user agent edit text.
1013 customUserAgentEditText.visibility = View.VISIBLE
1015 // Set the current user agent name as the text.
1016 customUserAgentEditText.setText(currentUserAgentName)
1018 // Set the background color to be blue.
1019 userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1023 // Show the user agent text view.
1024 userAgentTextView.visibility = View.VISIBLE
1026 // Set the text from the user agent data array, which has one less entry than the spinner, so the position must be decremented.
1027 userAgentTextView.text = userAgentDataArray[position - 1]
1029 // Hide the custom user agent edit text.
1030 customUserAgentEditText.visibility = View.GONE
1032 // Set the background color to be blue.
1033 userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1038 override fun onNothingSelected(parent: AdapterView<*>?) {
1043 // Set the font size spinner listener.
1044 fontSizeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1045 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1046 // Update the font size display options.
1047 if (position == 0) { // The system default font size has been selected.
1048 // Show the default font size text view.
1049 defaultFontSizeTextView.visibility = View.VISIBLE
1051 // Hide the custom font size edit text.
1052 customFontSizeEditText.visibility = View.GONE
1053 } else { // A custom font size has been selected.
1054 // Hide the default font size text view.
1055 defaultFontSizeTextView.visibility = View.GONE
1057 // Show the custom font size edit text.
1058 customFontSizeEditText.visibility = View.VISIBLE
1062 override fun onNothingSelected(parent: AdapterView<*>?) {
1067 // Set the swipe-to-refresh spinner listener.
1068 swipeToRefreshSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1069 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1070 // Update the icon and the visibility of the text view.
1072 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1073 // Set the icon color.
1074 swipeToRefreshImageView.isSelected = defaultSwipeToRefresh
1076 // Show the swipe-to-refresh text view.
1077 swipeToRefreshTextView.visibility = View.VISIBLE
1080 DomainsDatabaseHelper.ENABLED -> {
1081 // Set the icon color.
1082 swipeToRefreshImageView.isSelected = true
1084 // Hide the swipe-to-refresh text view.
1085 swipeToRefreshTextView.visibility = View.GONE
1088 DomainsDatabaseHelper.DISABLED -> {
1089 // Set the icon color.
1090 swipeToRefreshImageView.isSelected = false
1092 // Hide the swipe-to-refresh text view.
1093 swipeToRefreshTextView.visibility = View.GONE
1098 override fun onNothingSelected(parent: AdapterView<*>?) {
1103 // Set the WebView theme spinner listener.
1104 webViewThemeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1105 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1106 // Update the icon and the visibility of the WebView theme text view.
1108 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1109 // Set the icon color.
1110 when (appWebViewThemeEntryNumber) {
1111 DomainsDatabaseHelper.SYSTEM_DEFAULT -> webViewThemeImageView.isSelected = (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
1112 DomainsDatabaseHelper.LIGHT_THEME -> webViewThemeImageView.isSelected = true
1113 DomainsDatabaseHelper.DARK_THEME -> webViewThemeImageView.isSelected = false
1116 // Show the WebView theme text view.
1117 webViewThemeTextView.visibility = View.VISIBLE
1120 DomainsDatabaseHelper.LIGHT_THEME -> {
1121 // Set the icon color.
1122 webViewThemeImageView.isSelected = true
1124 // Hide the WebView theme text view.
1125 webViewThemeTextView.visibility = View.GONE
1128 DomainsDatabaseHelper.DARK_THEME -> {
1129 // Set the icon color.
1130 webViewThemeImageView.isSelected = false
1132 // Hide the WebView theme text view.
1133 webViewThemeTextView.visibility = View.GONE
1138 override fun onNothingSelected(parent: AdapterView<*>?) {
1143 // Set the wide viewport spinner listener.
1144 wideViewportSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1145 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1146 // Update the icon and the visibility of the wide viewport text view.
1148 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1149 // Set the icon color.
1150 wideViewportImageView.isSelected = defaultWideViewport
1152 // Show the wide viewport text view.
1153 wideViewportTextView.visibility = View.VISIBLE
1156 DomainsDatabaseHelper.ENABLED -> {
1157 // Set the icon color.
1158 wideViewportImageView.isSelected = true
1160 // Hide the wide viewport text view.
1161 wideViewportTextView.visibility = View.GONE
1164 DomainsDatabaseHelper.DISABLED -> {
1165 // Set the icon color.
1166 wideViewportImageView.isSelected = false
1168 // Hid ethe wide viewport text view.
1169 wideViewportTextView.visibility = View.GONE
1174 override fun onNothingSelected(parent: AdapterView<*>?) {
1179 // Set the display webpage images spinner listener.
1180 displayWebpageImagesSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1181 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1182 // Update the icon and the visibility of the display images text view.
1184 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1185 // Set the icon color.
1186 displayWebpageImagesImageView.isSelected = defaultDisplayWebpageImages
1188 // Show the display images text view.
1189 displayImagesTextView.visibility = View.VISIBLE
1192 DomainsDatabaseHelper.ENABLED -> {
1193 // Set the icon color.
1194 displayWebpageImagesImageView.isSelected = true
1196 // Hide the display images text view.
1197 displayImagesTextView.visibility = View.GONE
1200 DomainsDatabaseHelper.DISABLED -> {
1201 // Set the icon color.
1202 displayWebpageImagesImageView.isSelected = false
1204 // Hide the display images text view.
1205 displayImagesTextView.visibility = View.GONE
1210 override fun onNothingSelected(parent: AdapterView<*>?) {
1215 // Set the pinned SSL certificate switch listener.
1216 pinnedSslCertificateSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1217 // Update the icon color.
1218 pinnedSslCertificateImageView.isSelected = isChecked
1220 // Update the views.
1221 if (isChecked) { // SSL certificate pinning is enabled.
1222 // Update the visibility of the saved SSL certificate.
1223 if (savedSslIssuedToCNameString == null)
1224 savedSslCardView.visibility = View.GONE
1226 savedSslCardView.visibility = View.VISIBLE
1228 // Update the visibility of the current website SSL certificate.
1229 if (DomainsActivity.sslIssuedToCName == null) {
1230 // Hide the SSL certificate.
1231 currentSslCardView.visibility = View.GONE
1233 // Show the instruction.
1234 noCurrentWebsiteCertificateTextView.visibility = View.VISIBLE
1236 // Show the SSL certificate.
1237 currentSslCardView.visibility = View.VISIBLE
1239 // Hide the instruction.
1240 noCurrentWebsiteCertificateTextView.visibility = View.GONE
1243 // Set the status of the radio buttons.
1244 if (savedSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is displayed.
1245 // Check the saved SSL certificate radio button.
1246 savedSslCertificateRadioButton.isChecked = true
1248 // Uncheck the current website SSL certificate radio button.
1249 currentWebsiteCertificateRadioButton.isChecked = false
1251 // Set the background of the saved SSL certificate linear layout to be transparent.
1252 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1254 // Darken the background of the current website SSL certificate linear layout according to the theme.
1255 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1257 // Scroll to the current website SSL certificate card.
1258 savedSslCardView.parent.requestChildFocus(savedSslCardView, savedSslCardView)
1259 } else if (currentSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1260 // Check the current website SSL certificate radio button.
1261 currentWebsiteCertificateRadioButton.isChecked = true
1263 // Uncheck the saved SSL certificate radio button.
1264 savedSslCertificateRadioButton.isChecked = false
1266 // Set the background of the current website SSL certificate linear layout to be transparent.
1267 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1269 // Darken the background of the saved SSL certificate linear layout according to the theme.
1270 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1272 // Scroll to the current website SSL certificate card.
1273 currentSslCardView.parent.requestChildFocus(currentSslCardView, currentSslCardView)
1274 } else { // Neither SSL certificate is visible.
1275 // Uncheck both radio buttons.
1276 savedSslCertificateRadioButton.isChecked = false
1277 currentWebsiteCertificateRadioButton.isChecked = false
1279 // Scroll to the current website SSL certificate card.
1280 noCurrentWebsiteCertificateTextView.parent.requestChildFocus(noCurrentWebsiteCertificateTextView, noCurrentWebsiteCertificateTextView)
1282 } else { // SSL certificate pinning is disabled.
1283 // Hide the SSl certificates and instructions.
1284 savedSslCardView.visibility = View.GONE
1285 currentSslCardView.visibility = View.GONE
1286 noCurrentWebsiteCertificateTextView.visibility = View.GONE
1288 // Uncheck the radio buttons.
1289 savedSslCertificateRadioButton.isChecked = false
1290 currentWebsiteCertificateRadioButton.isChecked = false
1294 // Set the saved SSL card view listener.
1295 savedSslCardView.setOnClickListener {
1296 // Check the saved SSL certificate radio button.
1297 savedSslCertificateRadioButton.isChecked = true
1299 // Uncheck the current website SSL certificate radio button.
1300 currentWebsiteCertificateRadioButton.isChecked = false
1302 // Set the background of the saved SSL certificate linear layout to be transparent.
1303 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1305 // Darken the background of the current website SSL certificate linear layout.
1306 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1309 // Set the saved SSL certificate radio button listener.
1310 savedSslCertificateRadioButton.setOnClickListener {
1311 // Check the saved SSL certificate radio button.
1312 savedSslCertificateRadioButton.isChecked = true
1314 // Uncheck the current website SSL certificate radio button.
1315 currentWebsiteCertificateRadioButton.isChecked = false
1317 // Set the background of the saved SSL certificate linear layout to be transparent.
1318 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1320 // Darken the background of the current website SSL certificate linear layout.
1321 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1324 // Set the current SSL card view listener.
1325 currentSslCardView.setOnClickListener {
1326 // Check the current website SSL certificate radio button.
1327 currentWebsiteCertificateRadioButton.isChecked = true
1329 // Uncheck the saved SSL certificate radio button.
1330 savedSslCertificateRadioButton.isChecked = false
1332 // Set the background of the current website SSL certificate linear layout to be transparent.
1333 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1335 // Darken the background of the saved SSL certificate linear layout.
1336 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1339 // Set the current website certificate radio button listener.
1340 currentWebsiteCertificateRadioButton.setOnClickListener {
1341 // Check the current website SSL certificate radio button.
1342 currentWebsiteCertificateRadioButton.isChecked = true
1344 // Uncheck the saved SSL certificate radio button.
1345 savedSslCertificateRadioButton.isChecked = false
1347 // Set the background of the current website SSL certificate linear layout to be transparent.
1348 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1350 // Darken the background of the saved SSL certificate linear layout.
1351 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1354 // Set the pinned IP addresses switch listener.
1355 pinnedIpAddressesSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1356 // Update the icon color.
1357 pinnedIpAddressesImageView.isSelected = isChecked
1359 // Update the views.
1360 if (isChecked) { // IP addresses pinning is enabled.
1361 // Update the visibility of the saved IP addresses card view.
1362 if (savedIpAddresses == null)
1363 savedIpAddressesCardView.visibility = View.GONE
1365 savedIpAddressesCardView.visibility = View.VISIBLE
1367 // Show the current IP addresses card view.
1368 currentIpAddressesCardView.visibility = View.VISIBLE
1370 // Set the status of the radio buttons.
1371 if (savedIpAddressesCardView.visibility == View.VISIBLE) { // The saved IP addresses are visible.
1372 // Check the saved IP addresses radio button.
1373 savedIpAddressesRadioButton.isChecked = true
1375 // Uncheck the current IP addresses radio button.
1376 currentIpAddressesRadioButton.isChecked = false
1378 // Set the background of the saved IP addresses linear layout to be transparent.
1379 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1381 // Darken the background of the current IP addresses linear layout.
1382 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1383 } else { // The saved IP addresses are not visible.
1384 // Check the current IP addresses radio button.
1385 currentIpAddressesRadioButton.isChecked = true
1387 // Uncheck the saved IP addresses radio button.
1388 savedIpAddressesRadioButton.isChecked = false
1390 // Set the background of the current IP addresses linear layout to be transparent.
1391 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1393 // Darken the background of the saved IP addresses linear layout.
1394 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1397 // Scroll to the bottom of the card views.
1398 currentIpAddressesCardView.parent.requestChildFocus(currentIpAddressesCardView, currentIpAddressesCardView)
1399 } else { // IP addresses pinning is disabled.
1400 // Hide the IP addresses card views.
1401 savedIpAddressesCardView.visibility = View.GONE
1402 currentIpAddressesCardView.visibility = View.GONE
1404 // Uncheck the radio buttons.
1405 savedIpAddressesRadioButton.isChecked = false
1406 currentIpAddressesRadioButton.isChecked = false
1410 // Set the saved IP addresses card view listener.
1411 savedIpAddressesCardView.setOnClickListener {
1412 // Check the saved IP addresses radio button.
1413 savedIpAddressesRadioButton.isChecked = true
1415 // Uncheck the current website IP addresses radio button.
1416 currentIpAddressesRadioButton.isChecked = false
1418 // Set the background of the saved IP addresses linear layout to be transparent.
1419 savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1421 // Darken the background of the current IP addresses linear layout.
1422 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1425 // Set the saved IP addresses radio button listener.
1426 savedIpAddressesRadioButton.setOnClickListener {
1427 // Check the saved IP addresses radio button.
1428 savedIpAddressesRadioButton.isChecked = true
1430 // Uncheck the current website IP addresses radio button.
1431 currentIpAddressesRadioButton.isChecked = false
1433 // Set the background of the saved IP addresses linear layout to be transparent.
1434 savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1436 // Darken the background of the current IP addresses linear layout.
1437 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1440 // Set the current IP addresses card view listener.
1441 currentIpAddressesCardView.setOnClickListener {
1442 // Check the current IP addresses radio button.
1443 currentIpAddressesRadioButton.isChecked = true
1445 // Uncheck the saved IP addresses radio button.
1446 savedIpAddressesRadioButton.isChecked = false
1448 // Set the background of the current IP addresses linear layout to be transparent.
1449 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1451 // Darken the background of the saved IP addresses linear layout.
1452 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1455 // Set the current IP addresses radio button listener.
1456 currentIpAddressesRadioButton.setOnClickListener {
1457 // Check the current IP addresses radio button.
1458 currentIpAddressesRadioButton.isChecked = true
1460 // Uncheck the saved IP addresses radio button.
1461 savedIpAddressesRadioButton.isChecked = false
1463 // Set the background of the current IP addresses linear layout to be transparent.
1464 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1466 // Darken the background of the saved IP addresses linear layout.
1467 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1470 // Set the scroll Y.
1471 domainSettingsScrollView.post { domainSettingsScrollView.scrollY = scrollY }
1473 // Return the domain settings view.
1474 return domainSettingsView
1477 private fun checkDomainNameAgainstCertificate(domainName: String?, certificateCommonName: String?): Boolean {
1478 // Initialize the domain names match tracker.
1479 var domainNamesMatch = false
1481 // Check various wildcard permutations if the domain name and the certificate Common Name are not empty.
1482 if ((domainName != null) && (certificateCommonName != null)) {
1483 // Check if the domains match.
1484 if (domainName == certificateCommonName)
1485 domainNamesMatch = true
1487 // If the domain name starts with a wildcard, check the base domain against all the subdomains of the certificate Common Name.
1488 if (!domainNamesMatch && domainName.startsWith("*.") && domainName.length > 2) {
1489 // Remove the initial `*.`.
1490 val baseDomainName = domainName.substring(2)
1492 // Create a copy of the certificate Common Name to test subdomains.
1493 var certificateCommonNameSubdomain: String = certificateCommonName
1495 // Check all the subdomains in the certificate Common Name subdomain against the base domain name.
1496 while (!domainNamesMatch && certificateCommonNameSubdomain.contains(".")) { // Stop checking if the domain names match or if there are no more dots.
1497 // Test the certificate Common Name subdomain against the base domain name.
1498 if (certificateCommonNameSubdomain == baseDomainName)
1499 domainNamesMatch = true
1501 // Strip out the lowest subdomain of the certificate Common Name subdomain.
1502 certificateCommonNameSubdomain = try {
1503 certificateCommonNameSubdomain.substring(certificateCommonNameSubdomain.indexOf(".") + 1)
1504 } catch (e: IndexOutOfBoundsException) { // The certificate Common Name subdomain ends with a dot.
1510 // If the certificate Common Name starts with a wildcard, check the base common name against all the subdomains of the domain name.
1511 if (!domainNamesMatch && certificateCommonName.startsWith("*.") && certificateCommonName.length > 2) {
1512 // Remove the initial `*.`.
1513 val baseCertificateCommonName = certificateCommonName.substring(2)
1515 // Setup a copy of domain name to test subdomains.
1516 var domainNameSubdomain: String = domainName
1518 // Check all the subdomains in the domain name subdomain against the base certificate Common Name.
1519 while (!domainNamesMatch && domainNameSubdomain.contains(".") && domainNameSubdomain.length > 2) {
1520 // Test the domain name subdomain against the base certificate Common Name.
1521 if (domainNameSubdomain == baseCertificateCommonName)
1522 domainNamesMatch = true
1524 // Strip out the lowest subdomain of the domain name subdomain.
1525 domainNameSubdomain = try {
1526 domainNameSubdomain.substring(domainNameSubdomain.indexOf(".") + 1)
1527 } catch (e: IndexOutOfBoundsException) { // `domainNameSubdomain` ends with a dot.
1533 // If both names start with a wildcard, check if the root of one contains the root of the other.
1534 if (!domainNamesMatch && domainName.startsWith("*.") && domainName.length > 2 && certificateCommonName.startsWith("*.") && certificateCommonName.length > 2) {
1535 // Remove the wildcards.
1536 val rootDomainName = domainName.substring(2)
1537 val rootCertificateCommonName = certificateCommonName.substring(2)
1539 // Check if one name ends with the contents of the other. If so, there will be overlap in the their wildcard subdomains.
1540 if (rootDomainName.endsWith(rootCertificateCommonName) || rootCertificateCommonName.endsWith(rootDomainName))
1541 domainNamesMatch = true
1545 return domainNamesMatch