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.DomainsActivity
54 import com.stoutner.privacybrowser.activities.MainWebViewActivity
55 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper
57 import java.lang.IndexOutOfBoundsException
58 import java.text.DateFormat
59 import java.util.Calendar
62 class DomainSettingsFragment : Fragment() {
63 // Define the class variables.
64 private var scrollY = 0
67 // Define the public constants.
68 const val DATABASE_ID = "database_id"
69 const val SCROLL_Y = "scroll_y"
71 // Define the public variables. `databaseId` is public so it can be accessed from `DomainsActivity`.
75 override fun onCreate(savedInstanceState: Bundle?) {
76 // Run the default commands.
77 super.onCreate(savedInstanceState)
79 // Store the arguments in class variables.
80 databaseId = requireArguments().getInt(DATABASE_ID)
81 scrollY = requireArguments().getInt(SCROLL_Y)
84 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
85 // Inflate the layout. The fragment will take care of attaching the root automatically.
86 val domainSettingsView = inflater.inflate(R.layout.domain_settings_fragment, container, false)
88 // Get the current theme status.
89 val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
91 // Get a handle for the shared preference.
92 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
94 // Store the default settings.
95 val defaultUserAgentName = sharedPreferences.getString(getString(R.string.user_agent_key), getString(R.string.user_agent_default_value))
96 val defaultCustomUserAgentString = sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value))
97 val defaultFontSizeString = sharedPreferences.getString(getString(R.string.font_size_key), getString(R.string.font_size_default_value))
98 val defaultSwipeToRefresh = sharedPreferences.getBoolean(getString(R.string.swipe_to_refresh_key), true)
99 val defaultWebViewTheme = sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value))
100 val defaultWideViewport = sharedPreferences.getBoolean(getString(R.string.wide_viewport_key), true)
101 val defaultDisplayWebpageImages = sharedPreferences.getBoolean(getString(R.string.display_webpage_images_key), true)
103 // Get handles for the views.
104 val domainSettingsScrollView = domainSettingsView.findViewById<ScrollView>(R.id.domain_settings_scrollview)
105 val domainNameEditText = domainSettingsView.findViewById<EditText>(R.id.domain_settings_name_edittext)
106 val javaScriptImageView = domainSettingsView.findViewById<ImageView>(R.id.javascript_imageview)
107 val javaScriptSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.javascript_switch)
108 val cookiesImageView = domainSettingsView.findViewById<ImageView>(R.id.cookies_imageview)
109 val cookiesSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.cookies_switch)
110 val domStorageImageView = domainSettingsView.findViewById<ImageView>(R.id.dom_storage_imageview)
111 val domStorageSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.dom_storage_switch)
112 val formDataImageView = domainSettingsView.findViewById<ImageView>(R.id.form_data_imageview) // The form data views can be remove once the minimum API >= 26.
113 val formDataSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.form_data_switch) // The form data views can be remove once the minimum API >= 26.
114 val easyListImageView = domainSettingsView.findViewById<ImageView>(R.id.easylist_imageview)
115 val easyListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.easylist_switch)
116 val easyPrivacyImageView = domainSettingsView.findViewById<ImageView>(R.id.easyprivacy_imageview)
117 val easyPrivacySwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.easyprivacy_switch)
118 val fanboysAnnoyanceListImageView = domainSettingsView.findViewById<ImageView>(R.id.fanboys_annoyance_list_imageview)
119 val fanboysAnnoyanceListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.fanboys_annoyance_list_switch)
120 val fanboysSocialBlockingListImageView = domainSettingsView.findViewById<ImageView>(R.id.fanboys_social_blocking_list_imageview)
121 val fanboysSocialBlockingListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.fanboys_social_blocking_list_switch)
122 val ultraListImageView = domainSettingsView.findViewById<ImageView>(R.id.ultralist_imageview)
123 val ultraListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.ultralist_switch)
124 val ultraPrivacyImageView = domainSettingsView.findViewById<ImageView>(R.id.ultraprivacy_imageview)
125 val ultraPrivacySwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.ultraprivacy_switch)
126 val blockAllThirdPartyRequestsImageView = domainSettingsView.findViewById<ImageView>(R.id.block_all_third_party_requests_imageview)
127 val blockAllThirdPartyRequestsSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.block_all_third_party_requests_switch)
128 val userAgentSpinner = domainSettingsView.findViewById<Spinner>(R.id.user_agent_spinner)
129 val userAgentTextView = domainSettingsView.findViewById<TextView>(R.id.user_agent_textview)
130 val customUserAgentEditText = domainSettingsView.findViewById<EditText>(R.id.custom_user_agent_edittext)
131 val fontSizeSpinner = domainSettingsView.findViewById<Spinner>(R.id.font_size_spinner)
132 val defaultFontSizeTextView = domainSettingsView.findViewById<TextView>(R.id.default_font_size_textview)
133 val customFontSizeEditText = domainSettingsView.findViewById<EditText>(R.id.custom_font_size_edittext)
134 val swipeToRefreshImageView = domainSettingsView.findViewById<ImageView>(R.id.swipe_to_refresh_imageview)
135 val swipeToRefreshSpinner = domainSettingsView.findViewById<Spinner>(R.id.swipe_to_refresh_spinner)
136 val swipeToRefreshTextView = domainSettingsView.findViewById<TextView>(R.id.swipe_to_refresh_textview)
137 val webViewThemeLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.webview_theme_linearlayout)
138 val webViewThemeImageView = domainSettingsView.findViewById<ImageView>(R.id.webview_theme_imageview)
139 val webViewThemeSpinner = domainSettingsView.findViewById<Spinner>(R.id.webview_theme_spinner)
140 val webViewThemeTextView = domainSettingsView.findViewById<TextView>(R.id.webview_theme_textview)
141 val wideViewportImageView = domainSettingsView.findViewById<ImageView>(R.id.wide_viewport_imageview)
142 val wideViewportSpinner = domainSettingsView.findViewById<Spinner>(R.id.wide_viewport_spinner)
143 val wideViewportTextView = domainSettingsView.findViewById<TextView>(R.id.wide_viewport_textview)
144 val displayWebpageImagesImageView = domainSettingsView.findViewById<ImageView>(R.id.display_webpage_images_imageview)
145 val displayWebpageImagesSpinner = domainSettingsView.findViewById<Spinner>(R.id.display_webpage_images_spinner)
146 val displayImagesTextView = domainSettingsView.findViewById<TextView>(R.id.display_webpage_images_textview)
147 val pinnedSslCertificateImageView = domainSettingsView.findViewById<ImageView>(R.id.pinned_ssl_certificate_imageview)
148 val pinnedSslCertificateSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.pinned_ssl_certificate_switch)
149 val savedSslCardView = domainSettingsView.findViewById<CardView>(R.id.saved_ssl_certificate_cardview)
150 val savedSslCertificateLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.saved_ssl_certificate_linearlayout)
151 val savedSslCertificateRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.saved_ssl_certificate_radiobutton)
152 val savedSslIssuedToCNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_cname)
153 val savedSslIssuedToONameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_oname)
154 val savedSslIssuedToUNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_uname)
155 val savedSslIssuedByCNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_cname)
156 val savedSslIssuedByONameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_oname)
157 val savedSslIssuedByUNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_uname)
158 val savedSslStartDateTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_start_date)
159 val savedSslEndDateTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_end_date)
160 val currentSslCardView = domainSettingsView.findViewById<CardView>(R.id.current_website_certificate_cardview)
161 val currentWebsiteCertificateLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.current_website_certificate_linearlayout)
162 val currentWebsiteCertificateRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.current_website_certificate_radiobutton)
163 val currentSslIssuedToCNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_cname)
164 val currentSslIssuedToONameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_oname)
165 val currentSslIssuedToUNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_uname)
166 val currentSslIssuedByCNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_cname)
167 val currentSslIssuedByONameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_oname)
168 val currentSslIssuedByUNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_uname)
169 val currentSslStartDateTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_start_date)
170 val currentSslEndDateTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_end_date)
171 val noCurrentWebsiteCertificateTextView = domainSettingsView.findViewById<TextView>(R.id.no_current_website_certificate)
172 val pinnedIpAddressesImageView = domainSettingsView.findViewById<ImageView>(R.id.pinned_ip_addresses_imageview)
173 val pinnedIpAddressesSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.pinned_ip_addresses_switch)
174 val savedIpAddressesCardView = domainSettingsView.findViewById<CardView>(R.id.saved_ip_addresses_cardview)
175 val savedIpAddressesLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.saved_ip_addresses_linearlayout)
176 val savedIpAddressesRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.saved_ip_addresses_radiobutton)
177 val savedIpAddressesTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ip_addresses_textview)
178 val currentIpAddressesCardView = domainSettingsView.findViewById<CardView>(R.id.current_ip_addresses_cardview)
179 val currentIpAddressesLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.current_ip_addresses_linearlayout)
180 val currentIpAddressesRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.current_ip_addresses_radiobutton)
181 val currentIpAddressesTextView = domainSettingsView.findViewById<TextView>(R.id.current_ip_addresses_textview)
183 // Hide the WebView theme linear layout if the API < 29.
184 if (Build.VERSION.SDK_INT < 29)
185 webViewThemeLinearLayout.visibility = View.GONE
187 // Setup the pinned labels.
188 val cNameLabel = getString(R.string.common_name)
189 val oNameLabel = getString(R.string.organization)
190 val uNameLabel = getString(R.string.organizational_unit)
191 val startDateLabel = getString(R.string.start_date)
192 val endDateLabel = getString(R.string.end_date)
194 // Initialize the database handler.
195 val domainsDatabaseHelper = DomainsDatabaseHelper(requireContext())
197 // Get the database cursor for this ID.
198 val domainCursor = domainsDatabaseHelper.getCursorForId(databaseId)
200 // Move to the first row.
201 domainCursor.moveToFirst()
203 // Save the cursor entries as variables.
204 val domainNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME))
205 val javaScriptInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT))
206 val cookiesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES))
207 val domStorageInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE))
208 val formDataInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA)) // Form data can be remove once the minimum API >= 26.
209 val easyListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST))
210 val easyPrivacyInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY))
211 val fanboysAnnoyanceListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST))
212 val fanboysSocialBlockingListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST))
213 val ultraListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST))
214 val ultraPrivacyInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY))
215 val blockAllThirdPartyRequestsInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS))
216 val currentUserAgentName = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT))
217 val fontSizeInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE))
218 val swipeToRefreshInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH))
219 val webViewThemeInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME))
220 val wideViewportInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WIDE_VIEWPORT))
221 val displayImagesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DISPLAY_IMAGES))
222 val pinnedSslCertificateInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE))
223 val savedSslIssuedToCNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME))
224 val savedSslIssuedToONameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION))
225 val savedSslIssuedToUNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT))
226 val savedSslIssuedByCNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME))
227 val savedSslIssuedByONameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION))
228 val savedSslIssuedByUNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT))
229 val pinnedIpAddressesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_IP_ADDRESSES))
230 val savedIpAddresses = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.IP_ADDRESSES))
232 // Get the SSL dates from the database.
233 val savedSslStartDateLong = domainCursor.getLong(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_START_DATE))
234 val savedSslEndDateLong = domainCursor.getLong(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_END_DATE))
236 // Initialize the saved SSL certificate date variables.
237 var savedSslStartDate: Date? = null
238 var savedSslEndDate: Date? = null
240 // Only get the saved SSL certificate dates from the cursor if they are not set to `0`.
241 if (savedSslStartDateLong != 0L)
242 savedSslStartDate = Date(savedSslStartDateLong)
243 if (savedSslEndDateLong != 0L)
244 savedSslEndDate = Date(savedSslEndDateLong)
246 // Create array adapters for the spinners.
247 val translatedUserAgentArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.translated_domain_settings_user_agent_names, R.layout.spinner_item)
248 val fontSizeArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.font_size_array, R.layout.spinner_item)
249 val swipeToRefreshArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.swipe_to_refresh_array, R.layout.spinner_item)
250 val webViewThemeArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.webview_theme_array, R.layout.spinner_item)
251 val wideViewportArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.wide_viewport_array, R.layout.spinner_item)
252 val displayImagesArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.display_webpage_images_array, R.layout.spinner_item)
254 // Set the drop down view resource on the spinners.
255 translatedUserAgentArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
256 fontSizeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
257 swipeToRefreshArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
258 webViewThemeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
259 wideViewportArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
260 displayImagesArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
262 // Set the array adapters for the spinners.
263 userAgentSpinner.adapter = translatedUserAgentArrayAdapter
264 fontSizeSpinner.adapter = fontSizeArrayAdapter
265 swipeToRefreshSpinner.adapter = swipeToRefreshArrayAdapter
266 webViewThemeSpinner.adapter = webViewThemeArrayAdapter
267 wideViewportSpinner.adapter = wideViewportArrayAdapter
268 displayWebpageImagesSpinner.adapter = displayImagesArrayAdapter
270 // Create a spannable string builder for each TextView that needs multiple colors of text.
271 val savedSslIssuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString)
272 val savedSslIssuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + savedSslIssuedToONameString)
273 val savedSslIssuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + savedSslIssuedToUNameString)
274 val savedSslIssuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedByCNameString)
275 val savedSslIssuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + savedSslIssuedByONameString)
276 val savedSslIssuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + savedSslIssuedByUNameString)
278 // Create the date spannable string builders.
279 val savedSslStartDateStringBuilder: SpannableStringBuilder = if (savedSslStartDate == null)
280 SpannableStringBuilder(startDateLabel)
282 SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslStartDate))
284 val savedSslEndDateStringBuilder: SpannableStringBuilder = if (savedSslEndDate == null)
285 SpannableStringBuilder(endDateLabel)
287 SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslEndDate))
289 // Create the color spans.
290 val blueColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.alt_blue_text))
291 val redColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.red_text))
293 // Set the domain name from the the database cursor.
294 domainNameEditText.setText(domainNameString)
296 // Update the certificates' Common Name color when the domain name text changes.
297 domainNameEditText.addTextChangedListener(object : TextWatcher {
298 override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
302 override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
306 override fun afterTextChanged(s: Editable) {
307 // Get the new domain name.
308 val newDomainName = domainNameEditText.text.toString()
310 // Check the saved SSL certificate against the new domain name.
311 val savedSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, savedSslIssuedToCNameString)
313 // Create a spannable string builder for the saved certificate's Common Name.
314 val savedSslCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString)
316 // Format the saved certificate's Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
317 if (savedSslMatchesNewDomainName) {
318 savedSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
320 savedSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, savedSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
323 // Update the saved SSL issued to CName text view.
324 savedSslIssuedToCNameTextView.text = savedSslCNameStringBuilder
326 // Update the current website certificate if it exists.
327 if (DomainsActivity.sslIssuedToCName != null) {
328 // Check the current website certificate against the new domain name.
329 val currentSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, DomainsActivity.sslIssuedToCName)
331 // Create a spannable string builder for the current website certificate's Common Name.
332 val currentSslCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName)
334 // Format the current certificate Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
335 if (currentSslMatchesNewDomainName) {
336 currentSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
338 currentSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, currentSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
341 // Update the current SSL issued to CName text view.
342 currentSslIssuedToCNameTextView.text = currentSslCNameStringBuilder
347 // Set the switch positions.
348 javaScriptSwitch.isChecked = javaScriptInt == 1
349 cookiesSwitch.isChecked = cookiesInt == 1
350 domStorageSwitch.isChecked = domStorageInt == 1
351 formDataSwitch.isChecked = formDataInt == 1 // Form data can be removed once the minimum API >= 26.
352 easyListSwitch.isChecked = easyListInt == 1
353 easyPrivacySwitch.isChecked = easyPrivacyInt == 1
354 fanboysAnnoyanceListSwitch.isChecked = fanboysAnnoyanceListInt == 1
355 fanboysSocialBlockingListSwitch.isChecked = fanboysSocialBlockingListInt == 1
356 ultraListSwitch.isChecked = ultraListInt == 1
357 ultraPrivacySwitch.isChecked = ultraPrivacyInt == 1
358 blockAllThirdPartyRequestsSwitch.isChecked = blockAllThirdPartyRequestsInt == 1
359 pinnedSslCertificateSwitch.isChecked = pinnedSslCertificateInt == 1
360 pinnedIpAddressesSwitch.isChecked = pinnedIpAddressesInt == 1
362 // Set the switch icon colors.
363 cookiesImageView.isSelected = cookiesInt == 1
364 domStorageImageView.isSelected = domStorageInt == 1
365 formDataImageView.isSelected = formDataInt == 1 // Form data can be removed once the minimum API >= 26.
366 easyListImageView.isSelected = easyListInt == 1
367 easyPrivacyImageView.isSelected = easyPrivacyInt == 1
368 fanboysAnnoyanceListImageView.isSelected = fanboysAnnoyanceListInt == 1
369 fanboysSocialBlockingListImageView.isSelected = fanboysSocialBlockingListInt == 1
370 ultraListImageView.isSelected = ultraListInt == 1
371 ultraPrivacyImageView.isSelected = ultraPrivacyInt == 1
372 blockAllThirdPartyRequestsImageView.isSelected = blockAllThirdPartyRequestsInt == 1
373 pinnedSslCertificateImageView.isSelected = pinnedSslCertificateInt == 1
374 pinnedIpAddressesImageView.isSelected = pinnedIpAddressesInt == 1
376 // Set the JavaScript icon.
377 if (javaScriptInt == 1)
378 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null))
380 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null))
382 // Set the DOM storage switch status based on the JavaScript status.
383 domStorageSwitch.isEnabled = (javaScriptInt == 1)
385 // Set the DOM storage icon ghosted status based on the JavaScript status.
386 domStorageImageView.isEnabled = (javaScriptInt == 1)
388 // Set the form data visibility. Form data can be removed once the minimum API >= 26.
389 if (Build.VERSION.SDK_INT >= 26) {
390 // Hide the form data image view and switch.
391 formDataImageView.visibility = View.GONE
392 formDataSwitch.visibility = View.GONE
395 // Set Fanboy's Social Blocking List switch status based on the Annoyance List status.
396 fanboysSocialBlockingListSwitch.isEnabled = (fanboysAnnoyanceListInt == 0)
398 // Set the Social Blocking List icon ghosted status based on the Annoyance List status.
399 fanboysSocialBlockingListImageView.isEnabled = (fanboysAnnoyanceListInt == 0)
401 // Inflated a WebView to get the default user agent.
402 // `@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.
403 @SuppressLint("InflateParams") val bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false)
404 val bareWebView = bareWebViewLayout.findViewById<WebView>(R.id.bare_webview)
405 val webViewDefaultUserAgentString = bareWebView.settings.userAgentString
407 // Get a handle for the user agent array adapter. This array does not contain the `System default` entry.
408 val userAgentNamesArray = ArrayAdapter.createFromResource(requireContext(), R.array.user_agent_names, R.layout.spinner_item)
410 // Get the positions of the user agent and the default user agent.
411 val userAgentArrayPosition = userAgentNamesArray.getPosition(currentUserAgentName)
412 val defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName)
414 // Get a handle for the user agent data array. This array does not contain the `System default` entry.
415 val userAgentDataArray = resources.getStringArray(R.array.user_agent_data)
417 // Set the user agent text.
418 if (currentUserAgentName == getString(R.string.system_default_user_agent)) { // Use the system default user agent.
419 // Set the user agent according to the system default.
420 when (defaultUserAgentArrayPosition) {
421 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
422 MainWebViewActivity.UNRECOGNIZED_USER_AGENT -> userAgentTextView.text = defaultUserAgentName
424 // Display the WebView default user agent.
425 MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentTextView.text = webViewDefaultUserAgentString
427 // Display the custom user agent.
428 MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT -> userAgentTextView.text = defaultCustomUserAgentString
430 // Get the user agent string from the user agent data array.
431 else -> userAgentTextView.text = userAgentDataArray[defaultUserAgentArrayPosition]
433 } else if (userAgentArrayPosition == MainWebViewActivity.UNRECOGNIZED_USER_AGENT || currentUserAgentName == getString(R.string.custom_user_agent)) {
434 // 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.
435 // Set the user agent spinner to `Custom user agent`.
436 userAgentSpinner.setSelection(MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT)
438 // Hide the user agent text view.
439 userAgentTextView.visibility = View.GONE
441 // Show the custom user agent edit text and set the current user agent name as the text.
442 customUserAgentEditText.visibility = View.VISIBLE
443 customUserAgentEditText.setText(currentUserAgentName)
444 } else { // The user agent name contains one of the canonical user agents.
445 // 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.
446 userAgentSpinner.setSelection(userAgentArrayPosition + 1)
448 // Show the user agent text view.
449 userAgentTextView.visibility = View.VISIBLE
451 // Hide the custom user agent edit text.
452 customUserAgentEditText.visibility = View.GONE
454 // Set the user agent text.
455 if (userAgentArrayPosition == MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT) { // The WebView default user agent is selected.
456 // Display the WebView default user agent.
457 userAgentTextView.text = webViewDefaultUserAgentString
458 } else { // A user agent besides the default is selected.
459 // 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.
460 userAgentTextView.text = userAgentDataArray[userAgentArrayPosition + 1]
464 // Open the user agent spinner when the text view is clicked.
465 userAgentTextView.setOnClickListener { userAgentSpinner.performClick() }
467 // Display the font size settings.
468 if (fontSizeInt == 0) { // `0` is the code for system default font size.
469 // Set the font size to the system default.
470 fontSizeSpinner.setSelection(0)
472 // Show the default font size text view.
473 defaultFontSizeTextView.visibility = View.VISIBLE
475 // Hide the custom font size edit text.
476 customFontSizeEditText.visibility = View.GONE
478 // 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.
479 customFontSizeEditText.setText(defaultFontSizeString)
480 } else { // A custom font size is selected.
481 // Set the spinner to the custom font size.
482 fontSizeSpinner.setSelection(1)
484 // Hide the default font size text view.
485 defaultFontSizeTextView.visibility = View.GONE
487 // Show the custom font size edit text.
488 customFontSizeEditText.visibility = View.GONE
490 // Set the custom font size.
491 customFontSizeEditText.setText(fontSizeInt.toString())
494 // Initialize the default font size percentage string.
495 val defaultFontSizePercentageString = "$defaultFontSizeString%"
497 // Set the default font size text in the text view.
498 defaultFontSizeTextView.text = defaultFontSizePercentageString
500 // Open the font size spinner when the text view is clicked.
501 defaultFontSizeTextView.setOnClickListener { fontSizeSpinner.performClick() }
503 // Select the swipe-to-refresh selection in the spinner.
504 swipeToRefreshSpinner.setSelection(swipeToRefreshInt)
506 // Set the swipe-to-refresh text.
507 if (defaultSwipeToRefresh)
508 swipeToRefreshTextView.text = swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
510 swipeToRefreshTextView.text = swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
512 // Set the swipe-to-refresh icon and text view settings.
513 when (swipeToRefreshInt) {
514 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
515 // Set the icon color.
516 swipeToRefreshImageView.isSelected = defaultSwipeToRefresh
518 // Show the swipe-to-refresh text view.
519 swipeToRefreshTextView.visibility = View.VISIBLE
522 DomainsDatabaseHelper.ENABLED -> {
523 // Set the icon color.
524 swipeToRefreshImageView.isSelected = true
526 // Hide the swipe-to-refresh text view.
527 swipeToRefreshTextView.visibility = View.GONE
530 DomainsDatabaseHelper.DISABLED -> {
531 // Set the icon color.
532 swipeToRefreshImageView.isSelected = false
534 // Hide the swipe-to-refresh text view.
535 swipeToRefreshTextView.visibility = View.GONE
539 // Open the swipe-to-refresh spinner when the text view is clicked.
540 swipeToRefreshTextView.setOnClickListener { swipeToRefreshSpinner.performClick() }
542 // Get the WebView theme string arrays.
543 val webViewThemeStringArray = resources.getStringArray(R.array.webview_theme_array)
544 val webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values)
546 // Get the WebView theme entry number that matches the current WebView theme.
547 val appWebViewThemeEntryNumber = when (defaultWebViewTheme) {
548 webViewThemeEntryValuesStringArray[1] -> { 1 } // The light theme is selected.
549 webViewThemeEntryValuesStringArray[2] -> { 2 } // The dark theme is selected.
550 else -> { 0 } // The system default theme is selected.
553 // Select the WebView theme in the spinner.
554 webViewThemeSpinner.setSelection(webViewThemeInt)
556 // Set the WebView theme text.
557 if (appWebViewThemeEntryNumber == DomainsDatabaseHelper.SYSTEM_DEFAULT) { // The app WebView theme is system default.
558 // Set the text according to the current UI theme.
559 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
560 webViewThemeTextView.text = webViewThemeStringArray[DomainsDatabaseHelper.LIGHT_THEME]
562 webViewThemeTextView.text = webViewThemeStringArray[DomainsDatabaseHelper.DARK_THEME]
563 } else { // The app WebView theme is not system default.
564 // Set the text according to the app WebView theme.
565 webViewThemeTextView.text = webViewThemeStringArray[appWebViewThemeEntryNumber]
568 // Set the WebView theme icon and text visibility.
569 when (webViewThemeInt) {
570 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
571 // Set the icon color.
572 when (appWebViewThemeEntryNumber) {
573 DomainsDatabaseHelper.SYSTEM_DEFAULT -> webViewThemeImageView.isSelected = (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
574 DomainsDatabaseHelper.LIGHT_THEME -> webViewThemeImageView.isSelected = true
575 DomainsDatabaseHelper.DARK_THEME -> webViewThemeImageView.isSelected = false
578 // Show the WebView theme text view.
579 webViewThemeTextView.visibility = View.VISIBLE
582 DomainsDatabaseHelper.LIGHT_THEME -> {
583 // Set the icon color.
584 webViewThemeImageView.isSelected = true
586 // Hide the WebView theme text view.
587 webViewThemeTextView.visibility = View.GONE
590 DomainsDatabaseHelper.DARK_THEME -> {
591 // Set the icon color.
592 webViewThemeImageView.isSelected = false
594 // Hide the WebView theme text view.
595 webViewThemeTextView.visibility = View.GONE
599 // Open the WebView theme spinner when the text view is clicked.
600 webViewThemeTextView.setOnClickListener { webViewThemeSpinner.performClick() }
602 // Select the wide viewport in the spinner.
603 wideViewportSpinner.setSelection(wideViewportInt)
605 // Set the default wide viewport text.
606 if (defaultWideViewport)
607 wideViewportTextView.text = wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
608 else wideViewportTextView.text = wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
610 // Set the wide viewport icon and text view settings.
611 when (wideViewportInt) {
612 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
613 // Set the icon color.
614 wideViewportImageView.isSelected = defaultWideViewport
616 // Show the wide viewport text view.
617 wideViewportTextView.visibility = View.VISIBLE
620 DomainsDatabaseHelper.ENABLED -> {
621 // Set the icon color.
622 wideViewportImageView.isSelected = true
624 // Hide the wide viewport text view.
625 wideViewportTextView.visibility = View.GONE
628 DomainsDatabaseHelper.DISABLED -> {
629 // Set the icon color.
630 wideViewportImageView.isSelected = false
632 // Hide the wide viewport text view.
633 wideViewportTextView.visibility = View.GONE
637 // Open the wide viewport spinner when the text view is clicked.
638 wideViewportTextView.setOnClickListener { wideViewportSpinner.performClick() }
640 // Display the website images mode in the spinner.
641 displayWebpageImagesSpinner.setSelection(displayImagesInt)
643 // Set the default display images text.
644 if (defaultDisplayWebpageImages)
645 displayImagesTextView.text = displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
647 displayImagesTextView.text = displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
649 // Set the display website images icon and text view settings.
650 when (displayImagesInt) {
651 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
652 // Set the icon color.
653 displayWebpageImagesImageView.isSelected = defaultDisplayWebpageImages
655 // Show the display images text view.
656 displayImagesTextView.visibility = View.VISIBLE
659 DomainsDatabaseHelper.ENABLED -> {
660 // Set the icon color.
661 displayWebpageImagesImageView.isSelected = true
663 // Hide the display images text view.
664 displayImagesTextView.visibility = View.GONE
667 DomainsDatabaseHelper.DISABLED -> {
668 // Set the icon color.
669 displayWebpageImagesImageView.isSelected = false
671 // Hide the display images text view.
672 displayImagesTextView.visibility = View.GONE
676 // Open the display images spinner when the text view is clicked.
677 displayImagesTextView.setOnClickListener { displayWebpageImagesSpinner.performClick() }
679 // Store the current date.
680 val currentDate = Calendar.getInstance().time
682 // Setup the string builders to display the general certificate information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
683 savedSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, savedSslIssuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
684 savedSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, savedSslIssuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
685 savedSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslIssuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
686 savedSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, savedSslIssuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
687 savedSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, savedSslIssuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
689 // Check the certificate Common Name against the domain name.
690 val savedSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslIssuedToCNameString)
692 // Format the issued to Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
693 if (savedSslCommonNameMatchesDomainName)
694 savedSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
696 savedSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, savedSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
698 // Format the start date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
699 if (savedSslStartDate != null && savedSslStartDate.after(currentDate)) // The certificate start date is in the future.
700 savedSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, savedSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
701 else // The certificate start date is in the past.
702 savedSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, savedSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
704 // Format the end date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
705 if (savedSslEndDate != null && savedSslEndDate.before(currentDate)) // The certificate end date is in the past.
706 savedSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, savedSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
707 else // The certificate end date is in the future.
708 savedSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, savedSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
710 // Display the saved website SSL certificate strings.
711 savedSslIssuedToCNameTextView.text = savedSslIssuedToCNameStringBuilder
712 savedSslIssuedToONameTextView.text = savedSslIssuedToONameStringBuilder
713 savedSslIssuedToUNameTextView.text = savedSslIssuedToUNameStringBuilder
714 savedSslIssuedByCNameTextView.text = savedSslIssuedByCNameStringBuilder
715 savedSslIssuedByONameTextView.text = savedSslIssuedByONameStringBuilder
716 savedSslIssuedByUNameTextView.text = savedSslIssuedByUNameStringBuilder
717 savedSslStartDateTextView.text = savedSslStartDateStringBuilder
718 savedSslEndDateTextView.text = savedSslEndDateStringBuilder
720 // Populate the current website SSL certificate if there is one.
721 if (DomainsActivity.sslIssuedToCName != null) {
722 // Get dates from the raw long values.
723 val currentSslStartDate = Date(DomainsActivity.sslStartDateLong)
724 val currentSslEndDate = Date(DomainsActivity.sslEndDateLong)
726 // Create a spannable string builder for each text view that needs multiple colors of text.
727 val currentSslIssuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName)
728 val currentSslIssuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedToOName)
729 val currentSslIssuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedToUName)
730 val currentSslIssuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedByCName)
731 val currentSslIssuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedByOName)
732 val currentSslIssuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedByUName)
733 val currentSslStartDateStringBuilder = SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslStartDate))
734 val currentSslEndDateStringBuilder = SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslEndDate))
736 // Setup the string builders to display the general certificate information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
737 currentSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, currentSslIssuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
738 currentSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, currentSslIssuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
739 currentSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslIssuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
740 currentSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, currentSslIssuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
741 currentSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, currentSslIssuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
743 // Check the certificate Common Name against the domain name.
744 val currentSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, DomainsActivity.sslIssuedToCName)
746 // Format the issued to Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
747 if (currentSslCommonNameMatchesDomainName)
748 currentSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
750 currentSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, currentSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
752 // Format the start date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
753 if (currentSslStartDate.after(currentDate)) // The certificate start date is in the future.
754 currentSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, currentSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
755 else // The certificate start date is in the past.
756 currentSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, currentSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
758 // Format the end date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
759 if (currentSslEndDate.before(currentDate)) // The certificate end date is in the past.
760 currentSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, currentSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
761 else // The certificate end date is in the future.
762 currentSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, currentSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
764 // Display the current website SSL certificate strings.
765 currentSslIssuedToCNameTextView.text = currentSslIssuedToCNameStringBuilder
766 currentSslIssuedToONameTextView.text = currentSslIssuedToONameStringBuilder
767 currentSslIssuedToUNameTextView.text = currentSslIssuedToUNameStringBuilder
768 currentSslIssuedByCNameTextView.text = currentSslIssuedByCNameStringBuilder
769 currentSslIssuedByONameTextView.text = currentSslIssuedByONameStringBuilder
770 currentSslIssuedByUNameTextView.text = currentSslIssuedByUNameStringBuilder
771 currentSslStartDateTextView.text = currentSslStartDateStringBuilder
772 currentSslEndDateTextView.text = currentSslEndDateStringBuilder
775 // Set the initial display status of the SSL certificates card views.
776 if (pinnedSslCertificateSwitch.isChecked) { // An SSL certificate is pinned.
777 // Set the visibility of the saved SSL certificate.
778 if (savedSslIssuedToCNameString == null)
779 savedSslCardView.visibility = View.GONE
781 savedSslCardView.visibility = View.VISIBLE
783 // Set the visibility of the current website SSL certificate.
784 if (DomainsActivity.sslIssuedToCName == null) { // There is no current SSL certificate.
785 // Hide the SSL certificate.
786 currentSslCardView.visibility = View.GONE
788 // Show the instruction.
789 noCurrentWebsiteCertificateTextView.visibility = View.VISIBLE
790 } else { // There is a current SSL certificate.
791 // Show the SSL certificate.
792 currentSslCardView.visibility = View.VISIBLE
794 // Hide the instruction.
795 noCurrentWebsiteCertificateTextView.visibility = View.GONE
798 // Set the status of the radio buttons and the card view backgrounds.
799 if (savedSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is displayed.
800 // Check the saved SSL certificate radio button.
801 savedSslCertificateRadioButton.isChecked = true
803 // Uncheck the current website SSL certificate radio button.
804 currentWebsiteCertificateRadioButton.isChecked = false
806 // Darken the background of the current website SSL certificate linear layout.
807 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
808 } else if (currentSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is hidden but the current website SSL certificate is visible.
809 // Check the current website SSL certificate radio button.
810 currentWebsiteCertificateRadioButton.isChecked = true
812 // Uncheck the saved SSL certificate radio button.
813 savedSslCertificateRadioButton.isChecked = false
814 } else { // Neither SSL certificate is visible.
815 // Uncheck both radio buttons.
816 savedSslCertificateRadioButton.isChecked = false
817 currentWebsiteCertificateRadioButton.isChecked = false
819 } else { // An SSL certificate is not pinned.
820 // Hide the SSl certificates and instructions.
821 savedSslCardView.visibility = View.GONE
822 currentSslCardView.visibility = View.GONE
823 noCurrentWebsiteCertificateTextView.visibility = View.GONE
825 // Uncheck the radio buttons.
826 savedSslCertificateRadioButton.isChecked = false
827 currentWebsiteCertificateRadioButton.isChecked = false
830 // Populate the saved and current IP addresses.
831 savedIpAddressesTextView.text = savedIpAddresses
832 currentIpAddressesTextView.text = DomainsActivity.currentIpAddresses
834 // Set the initial display status of the IP addresses card views.
835 if (pinnedIpAddressesSwitch.isChecked) { // IP addresses are pinned.
836 // Set the visibility of the saved IP addresses.
837 if (savedIpAddresses == null) // There are no saved IP addresses.
838 savedIpAddressesCardView.visibility = View.GONE
839 else // There are saved IP addresses.
840 savedIpAddressesCardView.visibility = View.VISIBLE
842 // Set the visibility of the current IP addresses.
843 currentIpAddressesCardView.visibility = View.VISIBLE
845 // Set the status of the radio buttons and the card view backgrounds.
846 if (savedIpAddressesCardView.visibility == View.VISIBLE) { // The saved IP addresses are displayed.
847 // Check the saved IP addresses radio button.
848 savedIpAddressesRadioButton.isChecked = true
850 // Uncheck the current IP addresses radio button.
851 currentIpAddressesRadioButton.isChecked = false
853 // Darken the background of the current IP addresses linear layout.
854 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
855 } else { // The saved IP addresses are hidden.
856 // Check the current IP addresses radio button.
857 currentIpAddressesRadioButton.isChecked = true
859 // Uncheck the saved IP addresses radio button.
860 savedIpAddressesRadioButton.isChecked = false
862 } else { // IP addresses are not pinned.
863 // Hide the IP addresses card views.
864 savedIpAddressesCardView.visibility = View.GONE
865 currentIpAddressesCardView.visibility = View.GONE
867 // Uncheck the radio buttons.
868 savedIpAddressesRadioButton.isChecked = false
869 currentIpAddressesRadioButton.isChecked = false
873 // Set the JavaScript switch listener.
874 javaScriptSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
875 // Update the JavaScript icon.
877 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null))
879 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null))
881 // Set the DOM storage switch status.
882 domStorageSwitch.isEnabled = isChecked
884 // Set the DOM storage ghosted icon status.
885 domStorageImageView.isEnabled = isChecked
888 // Set the cookies switch listener.
889 cookiesSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
890 // Update the icon color.
891 cookiesImageView.isSelected = isChecked
894 // Set the DOM Storage switch listener.
895 domStorageSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
896 // Update the icon color.
897 domStorageImageView.isSelected = isChecked
900 // Set the form data switch listener. It can be removed once the minimum API >= 26.
901 if (Build.VERSION.SDK_INT < 26) {
902 formDataSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
903 // Update the icon color.
904 formDataImageView.isSelected = isChecked
908 // Set the EasyList switch listener.
909 easyListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
910 // Update the icon color.
911 easyListImageView.isSelected = isChecked
914 // Set the EasyPrivacy switch listener.
915 easyPrivacySwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
916 // Update the icon color.
917 easyPrivacyImageView.isSelected = isChecked
920 // Set the Fanboy's Annoyance List switch listener.
921 fanboysAnnoyanceListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
922 // Update the icon color.
923 fanboysAnnoyanceListImageView.isSelected = isChecked
925 // Set Fanboy's Social Blocking List switch position.
926 fanboysSocialBlockingListSwitch.isEnabled = !isChecked
928 // Set the Social Blocking List icon ghosted status.
929 fanboysSocialBlockingListImageView.isEnabled = !isChecked
932 // Set the Fanboy's Social Blocking List switch listener.
933 fanboysSocialBlockingListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
934 // Update the icon color.
935 fanboysSocialBlockingListImageView.isSelected = isChecked
938 // Set the UltraList switch listener.
939 ultraListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
940 // Update the icon color.
941 ultraListImageView.isSelected = isChecked
944 // Set the UltraPrivacy switch listener.
945 ultraPrivacySwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
946 // Update the icon color.
947 ultraPrivacyImageView.isSelected = isChecked
950 // Set the block all third-party requests switch listener.
951 blockAllThirdPartyRequestsSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
952 // Update the icon color.
953 blockAllThirdPartyRequestsImageView.isSelected = isChecked
956 // Set the user agent spinner listener.
957 userAgentSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
958 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
959 // Set the new user agent.
961 MainWebViewActivity.DOMAINS_SYSTEM_DEFAULT_USER_AGENT -> {
962 // Show the user agent text view.
963 userAgentTextView.visibility = View.VISIBLE
965 // Hide the custom user agent edit text.
966 customUserAgentEditText.visibility = View.GONE
968 // Set the user text.
969 when (defaultUserAgentArrayPosition) {
970 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
971 MainWebViewActivity.UNRECOGNIZED_USER_AGENT -> userAgentTextView.text = defaultUserAgentName
973 // Display the `WebView` default user agent.
974 MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentTextView.text = webViewDefaultUserAgentString
976 // Display the custom user agent.
977 MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT -> userAgentTextView.text = defaultCustomUserAgentString
979 // Get the user agent string from the user agent data array.
980 else -> userAgentTextView.text = userAgentDataArray[defaultUserAgentArrayPosition]
984 MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT -> {
985 // Show the user agent text view.
986 userAgentTextView.visibility = View.VISIBLE
988 // Set the user agent text.
989 userAgentTextView.text = webViewDefaultUserAgentString
991 // Hide the custom user agent EditTex.
992 customUserAgentEditText.visibility = View.GONE
995 MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT -> {
996 // Hide the user agent TextView.
997 userAgentTextView.visibility = View.GONE
999 // Show the custom user agent edit text.
1000 customUserAgentEditText.visibility = View.VISIBLE
1002 // Set the current user agent name as the text.
1003 customUserAgentEditText.setText(currentUserAgentName)
1007 // Show the user agent text view.
1008 userAgentTextView.visibility = View.VISIBLE
1010 // Set the text from the user agent data array, which has one less entry than the spinner, so the position must be decremented.
1011 userAgentTextView.text = userAgentDataArray[position - 1]
1013 // Hide the custom user agent edit text.
1014 customUserAgentEditText.visibility = View.GONE
1019 override fun onNothingSelected(parent: AdapterView<*>?) {
1024 // Set the font size spinner listener.
1025 fontSizeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1026 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1027 // Update the font size display options.
1028 if (position == 0) { // The system default font size has been selected.
1029 // Show the default font size text view.
1030 defaultFontSizeTextView.visibility = View.VISIBLE
1032 // Hide the custom font size edit text.
1033 customFontSizeEditText.visibility = View.GONE
1034 } else { // A custom font size has been selected.
1035 // Hide the default font size text view.
1036 defaultFontSizeTextView.visibility = View.GONE
1038 // Show the custom font size edit text.
1039 customFontSizeEditText.visibility = View.VISIBLE
1043 override fun onNothingSelected(parent: AdapterView<*>?) {
1048 // Set the swipe-to-refresh spinner listener.
1049 swipeToRefreshSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1050 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1051 // Update the icon and the visibility of the text view.
1053 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1054 // Set the icon color.
1055 swipeToRefreshImageView.isSelected = defaultSwipeToRefresh
1057 // Show the swipe-to-refresh text view.
1058 swipeToRefreshTextView.visibility = View.VISIBLE
1061 DomainsDatabaseHelper.ENABLED -> {
1062 // Set the icon color.
1063 swipeToRefreshImageView.isSelected = true
1065 // Hide the swipe-to-refresh text view.
1066 swipeToRefreshTextView.visibility = View.GONE
1069 DomainsDatabaseHelper.DISABLED -> {
1070 // Set the icon color.
1071 swipeToRefreshImageView.isSelected = false
1073 // Hide the swipe-to-refresh text view.
1074 swipeToRefreshTextView.visibility = View.GONE
1079 override fun onNothingSelected(parent: AdapterView<*>?) {
1084 // Set the WebView theme spinner listener.
1085 webViewThemeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1086 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1087 // Update the icon and the visibility of the WebView theme text view.
1089 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1090 // Set the icon color.
1091 when (appWebViewThemeEntryNumber) {
1092 DomainsDatabaseHelper.SYSTEM_DEFAULT -> webViewThemeImageView.isSelected = (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
1093 DomainsDatabaseHelper.LIGHT_THEME -> webViewThemeImageView.isSelected = true
1094 DomainsDatabaseHelper.DARK_THEME -> webViewThemeImageView.isSelected = false
1097 // Show the WebView theme text view.
1098 webViewThemeTextView.visibility = View.VISIBLE
1101 DomainsDatabaseHelper.LIGHT_THEME -> {
1102 // Set the icon color.
1103 webViewThemeImageView.isSelected = true
1105 // Hide the WebView theme text view.
1106 webViewThemeTextView.visibility = View.GONE
1109 DomainsDatabaseHelper.DARK_THEME -> {
1110 // Set the icon color.
1111 webViewThemeImageView.isSelected = false
1113 // Hide the WebView theme text view.
1114 webViewThemeTextView.visibility = View.GONE
1119 override fun onNothingSelected(parent: AdapterView<*>?) {
1124 // Set the wide viewport spinner listener.
1125 wideViewportSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1126 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1127 // Update the icon and the visibility of the wide viewport text view.
1129 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1130 // Set the icon color.
1131 wideViewportImageView.isSelected = defaultWideViewport
1133 // Show the wide viewport text view.
1134 wideViewportTextView.visibility = View.VISIBLE
1137 DomainsDatabaseHelper.ENABLED -> {
1138 // Set the icon color.
1139 wideViewportImageView.isSelected = true
1141 // Hide the wide viewport text view.
1142 wideViewportTextView.visibility = View.GONE
1145 DomainsDatabaseHelper.DISABLED -> {
1146 // Set the icon color.
1147 wideViewportImageView.isSelected = false
1149 // Hid ethe wide viewport text view.
1150 wideViewportTextView.visibility = View.GONE
1155 override fun onNothingSelected(parent: AdapterView<*>?) {
1160 // Set the display webpage images spinner listener.
1161 displayWebpageImagesSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1162 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1163 // Update the icon and the visibility of the display images text view.
1165 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1166 // Set the icon color.
1167 displayWebpageImagesImageView.isSelected = defaultDisplayWebpageImages
1169 // Show the display images text view.
1170 displayImagesTextView.visibility = View.VISIBLE
1173 DomainsDatabaseHelper.ENABLED -> {
1174 // Set the icon color.
1175 displayWebpageImagesImageView.isSelected = true
1177 // Hide the display images text view.
1178 displayImagesTextView.visibility = View.GONE
1181 DomainsDatabaseHelper.DISABLED -> {
1182 // Set the icon color.
1183 displayWebpageImagesImageView.isSelected = false
1185 // Hide the display images text view.
1186 displayImagesTextView.visibility = View.GONE
1191 override fun onNothingSelected(parent: AdapterView<*>?) {
1196 // Set the pinned SSL certificate switch listener.
1197 pinnedSslCertificateSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1198 // Update the icon color.
1199 pinnedSslCertificateImageView.isSelected = isChecked
1201 // Update the views.
1202 if (isChecked) { // SSL certificate pinning is enabled.
1203 // Update the visibility of the saved SSL certificate.
1204 if (savedSslIssuedToCNameString == null)
1205 savedSslCardView.visibility = View.GONE
1207 savedSslCardView.visibility = View.VISIBLE
1209 // Update the visibility of the current website SSL certificate.
1210 if (DomainsActivity.sslIssuedToCName == null) {
1211 // Hide the SSL certificate.
1212 currentSslCardView.visibility = View.GONE
1214 // Show the instruction.
1215 noCurrentWebsiteCertificateTextView.visibility = View.VISIBLE
1217 // Show the SSL certificate.
1218 currentSslCardView.visibility = View.VISIBLE
1220 // Hide the instruction.
1221 noCurrentWebsiteCertificateTextView.visibility = View.GONE
1224 // Set the status of the radio buttons.
1225 if (savedSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is displayed.
1226 // Check the saved SSL certificate radio button.
1227 savedSslCertificateRadioButton.isChecked = true
1229 // Uncheck the current website SSL certificate radio button.
1230 currentWebsiteCertificateRadioButton.isChecked = false
1232 // Set the background of the saved SSL certificate linear layout to be transparent.
1233 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1235 // Darken the background of the current website SSL certificate linear layout according to the theme.
1236 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1238 // Scroll to the current website SSL certificate card.
1239 savedSslCardView.parent.requestChildFocus(savedSslCardView, savedSslCardView)
1240 } else if (currentSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1241 // Check the current website SSL certificate radio button.
1242 currentWebsiteCertificateRadioButton.isChecked = true
1244 // Uncheck the saved SSL certificate radio button.
1245 savedSslCertificateRadioButton.isChecked = false
1247 // Set the background of the current website SSL certificate linear layout to be transparent.
1248 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1250 // Darken the background of the saved SSL certificate linear layout according to the theme.
1251 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1253 // Scroll to the current website SSL certificate card.
1254 currentSslCardView.parent.requestChildFocus(currentSslCardView, currentSslCardView)
1255 } else { // Neither SSL certificate is visible.
1256 // Uncheck both radio buttons.
1257 savedSslCertificateRadioButton.isChecked = false
1258 currentWebsiteCertificateRadioButton.isChecked = false
1260 // Scroll to the current website SSL certificate card.
1261 noCurrentWebsiteCertificateTextView.parent.requestChildFocus(noCurrentWebsiteCertificateTextView, noCurrentWebsiteCertificateTextView)
1263 } else { // SSL certificate pinning is disabled.
1264 // Hide the SSl certificates and instructions.
1265 savedSslCardView.visibility = View.GONE
1266 currentSslCardView.visibility = View.GONE
1267 noCurrentWebsiteCertificateTextView.visibility = View.GONE
1269 // Uncheck the radio buttons.
1270 savedSslCertificateRadioButton.isChecked = false
1271 currentWebsiteCertificateRadioButton.isChecked = false
1275 // Set the saved SSL card view listener.
1276 savedSslCardView.setOnClickListener {
1277 // Check the saved SSL certificate radio button.
1278 savedSslCertificateRadioButton.isChecked = true
1280 // Uncheck the current website SSL certificate radio button.
1281 currentWebsiteCertificateRadioButton.isChecked = false
1283 // Set the background of the saved SSL certificate linear layout to be transparent.
1284 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1286 // Darken the background of the current website SSL certificate linear layout.
1287 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1290 // Set the saved SSL certificate radio button listener.
1291 savedSslCertificateRadioButton.setOnClickListener {
1292 // Check the saved SSL certificate radio button.
1293 savedSslCertificateRadioButton.isChecked = true
1295 // Uncheck the current website SSL certificate radio button.
1296 currentWebsiteCertificateRadioButton.isChecked = false
1298 // Set the background of the saved SSL certificate linear layout to be transparent.
1299 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1301 // Darken the background of the current website SSL certificate linear layout.
1302 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1305 // Set the current SSL card view listener.
1306 currentSslCardView.setOnClickListener {
1307 // Check the current website SSL certificate radio button.
1308 currentWebsiteCertificateRadioButton.isChecked = true
1310 // Uncheck the saved SSL certificate radio button.
1311 savedSslCertificateRadioButton.isChecked = false
1313 // Set the background of the current website SSL certificate linear layout to be transparent.
1314 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1316 // Darken the background of the saved SSL certificate linear layout.
1317 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1320 // Set the current website certificate radio button listener.
1321 currentWebsiteCertificateRadioButton.setOnClickListener {
1322 // Check the current website SSL certificate radio button.
1323 currentWebsiteCertificateRadioButton.isChecked = true
1325 // Uncheck the saved SSL certificate radio button.
1326 savedSslCertificateRadioButton.isChecked = false
1328 // Set the background of the current website SSL certificate linear layout to be transparent.
1329 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1331 // Darken the background of the saved SSL certificate linear layout.
1332 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1335 // Set the pinned IP addresses switch listener.
1336 pinnedIpAddressesSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1337 // Update the icon color.
1338 pinnedIpAddressesImageView.isSelected = isChecked
1340 // Update the views.
1341 if (isChecked) { // IP addresses pinning is enabled.
1342 // Update the visibility of the saved IP addresses card view.
1343 if (savedIpAddresses == null)
1344 savedIpAddressesCardView.visibility = View.GONE
1346 savedIpAddressesCardView.visibility = View.VISIBLE
1348 // Show the current IP addresses card view.
1349 currentIpAddressesCardView.visibility = View.VISIBLE
1351 // Set the status of the radio buttons.
1352 if (savedIpAddressesCardView.visibility == View.VISIBLE) { // The saved IP addresses are visible.
1353 // Check the saved IP addresses radio button.
1354 savedIpAddressesRadioButton.isChecked = true
1356 // Uncheck the current IP addresses radio button.
1357 currentIpAddressesRadioButton.isChecked = false
1359 // Set the background of the saved IP addresses linear layout to be transparent.
1360 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1362 // Darken the background of the current IP addresses linear layout.
1363 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1364 } else { // The saved IP addresses are not visible.
1365 // Check the current IP addresses radio button.
1366 currentIpAddressesRadioButton.isChecked = true
1368 // Uncheck the saved IP addresses radio button.
1369 savedIpAddressesRadioButton.isChecked = false
1371 // Set the background of the current IP addresses linear layout to be transparent.
1372 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1374 // Darken the background of the saved IP addresses linear layout.
1375 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1378 // Scroll to the bottom of the card views.
1379 currentIpAddressesCardView.parent.requestChildFocus(currentIpAddressesCardView, currentIpAddressesCardView)
1380 } else { // IP addresses pinning is disabled.
1381 // Hide the IP addresses card views.
1382 savedIpAddressesCardView.visibility = View.GONE
1383 currentIpAddressesCardView.visibility = View.GONE
1385 // Uncheck the radio buttons.
1386 savedIpAddressesRadioButton.isChecked = false
1387 currentIpAddressesRadioButton.isChecked = false
1391 // Set the saved IP addresses card view listener.
1392 savedIpAddressesCardView.setOnClickListener {
1393 // Check the saved IP addresses radio button.
1394 savedIpAddressesRadioButton.isChecked = true
1396 // Uncheck the current website IP addresses radio button.
1397 currentIpAddressesRadioButton.isChecked = false
1399 // Set the background of the saved IP addresses linear layout to be transparent.
1400 savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1402 // Darken the background of the current IP addresses linear layout.
1403 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1406 // Set the saved IP addresses radio button listener.
1407 savedIpAddressesRadioButton.setOnClickListener {
1408 // Check the saved IP addresses radio button.
1409 savedIpAddressesRadioButton.isChecked = true
1411 // Uncheck the current website IP addresses radio button.
1412 currentIpAddressesRadioButton.isChecked = false
1414 // Set the background of the saved IP addresses linear layout to be transparent.
1415 savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1417 // Darken the background of the current IP addresses linear layout.
1418 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1421 // Set the current IP addresses card view listener.
1422 currentIpAddressesCardView.setOnClickListener {
1423 // Check the current IP addresses radio button.
1424 currentIpAddressesRadioButton.isChecked = true
1426 // Uncheck the saved IP addresses radio button.
1427 savedIpAddressesRadioButton.isChecked = false
1429 // Set the background of the current IP addresses linear layout to be transparent.
1430 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1432 // Darken the background of the saved IP addresses linear layout.
1433 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1436 // Set the current IP addresses radio button listener.
1437 currentIpAddressesRadioButton.setOnClickListener {
1438 // Check the current IP addresses radio button.
1439 currentIpAddressesRadioButton.isChecked = true
1441 // Uncheck the saved IP addresses radio button.
1442 savedIpAddressesRadioButton.isChecked = false
1444 // Set the background of the current IP addresses linear layout to be transparent.
1445 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1447 // Darken the background of the saved IP addresses linear layout.
1448 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1451 // Set the scroll Y.
1452 domainSettingsScrollView.post { domainSettingsScrollView.scrollY = scrollY }
1454 // Return the domain settings view.
1455 return domainSettingsView
1458 private fun checkDomainNameAgainstCertificate(domainName: String?, certificateCommonName: String?): Boolean {
1459 // Initialize the domain names match tracker.
1460 var domainNamesMatch = false
1462 // Check various wildcard permutations if the domain name and the certificate Common Name are not empty.
1463 if ((domainName != null) && (certificateCommonName != null)) {
1464 // Check if the domains match.
1465 if (domainName == certificateCommonName)
1466 domainNamesMatch = true
1468 // If the domain name starts with a wildcard, check the base domain against all the subdomains of the certificate Common Name.
1469 if (!domainNamesMatch && domainName.startsWith("*.") && domainName.length > 2) {
1470 // Remove the initial `*.`.
1471 val baseDomainName = domainName.substring(2)
1473 // Create a copy of the certificate Common Name to test subdomains.
1474 var certificateCommonNameSubdomain: String = certificateCommonName
1476 // Check all the subdomains in the certificate Common Name subdomain against the base domain name.
1477 while (!domainNamesMatch && certificateCommonNameSubdomain.contains(".")) { // Stop checking if the domain names match or if there are no more dots.
1478 // Test the certificate Common Name subdomain against the base domain name.
1479 if (certificateCommonNameSubdomain == baseDomainName)
1480 domainNamesMatch = true
1482 // Strip out the lowest subdomain of the certificate Common Name subdomain.
1483 certificateCommonNameSubdomain = try {
1484 certificateCommonNameSubdomain.substring(certificateCommonNameSubdomain.indexOf(".") + 1)
1485 } catch (e: IndexOutOfBoundsException) { // The certificate Common Name subdomain ends with a dot.
1491 // If the certificate Common Name starts with a wildcard, check the base common name against all the subdomains of the domain name.
1492 if (!domainNamesMatch && certificateCommonName.startsWith("*.") && certificateCommonName.length > 2) {
1493 // Remove the initial `*.`.
1494 val baseCertificateCommonName = certificateCommonName.substring(2)
1496 // Setup a copy of domain name to test subdomains.
1497 var domainNameSubdomain: String = domainName
1499 // Check all the subdomains in the domain name subdomain against the base certificate Common Name.
1500 while (!domainNamesMatch && domainNameSubdomain.contains(".") && domainNameSubdomain.length > 2) {
1501 // Test the domain name subdomain against the base certificate Common Name.
1502 if (domainNameSubdomain == baseCertificateCommonName)
1503 domainNamesMatch = true
1505 // Strip out the lowest subdomain of the domain name subdomain.
1506 domainNameSubdomain = try {
1507 domainNameSubdomain.substring(domainNameSubdomain.indexOf(".") + 1)
1508 } catch (e: IndexOutOfBoundsException) { // `domainNameSubdomain` ends with a dot.
1514 // If both names start with a wildcard, check if the root of one contains the root of the other.
1515 if (!domainNamesMatch && domainName.startsWith("*.") && domainName.length > 2 && certificateCommonName.startsWith("*.") && certificateCommonName.length > 2) {
1516 // Remove the wildcards.
1517 val rootDomainName = domainName.substring(2)
1518 val rootCertificateCommonName = certificateCommonName.substring(2)
1520 // Check if one name ends with the contents of the other. If so, there will be overlap in the their wildcard subdomains.
1521 if (rootDomainName.endsWith(rootCertificateCommonName) || rootCertificateCommonName.endsWith(rootDomainName))
1522 domainNamesMatch = true
1526 return domainNamesMatch