2 * Copyright 2017-2022 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
6 * Privacy Browser Android is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * Privacy Browser Android is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Browser Android. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacybrowser.fragments
22 import android.annotation.SuppressLint
23 import android.content.res.Configuration
24 import android.os.Build
25 import android.os.Bundle
26 import android.text.Editable
27 import android.text.SpannableStringBuilder
28 import android.text.Spanned
29 import android.text.TextWatcher
30 import android.text.style.ForegroundColorSpan
31 import android.view.LayoutInflater
32 import android.view.View
33 import android.view.ViewGroup
34 import android.webkit.WebView
35 import android.widget.AdapterView
36 import android.widget.ArrayAdapter
37 import android.widget.CompoundButton
38 import android.widget.EditText
39 import android.widget.ImageView
40 import android.widget.LinearLayout
41 import android.widget.RadioButton
42 import android.widget.ScrollView
43 import android.widget.Spinner
44 import android.widget.TextView
46 import androidx.appcompat.widget.SwitchCompat
47 import androidx.cardview.widget.CardView
48 import androidx.core.content.res.ResourcesCompat
49 import androidx.fragment.app.Fragment
50 import androidx.preference.PreferenceManager
52 import com.stoutner.privacybrowser.R
53 import com.stoutner.privacybrowser.activities.DomainsActivity
54 import com.stoutner.privacybrowser.activities.MainWebViewActivity
55 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper
57 import java.lang.IndexOutOfBoundsException
58 import java.text.DateFormat
59 import java.util.Calendar
62 class DomainSettingsFragment : Fragment() {
63 // Define the class variables.
64 private var scrollY = 0
67 // Define the public constants.
68 const val DATABASE_ID = "database_id"
69 const val SCROLL_Y = "scroll_y"
71 // Define the public variables. `databaseId` is public so it can be accessed from `DomainsActivity`.
75 override fun onCreate(savedInstanceState: Bundle?) {
76 // Run the default commands.
77 super.onCreate(savedInstanceState)
79 // Store the arguments in class variables.
80 databaseId = requireArguments().getInt(DATABASE_ID)
81 scrollY = requireArguments().getInt(SCROLL_Y)
84 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
85 // Inflate the layout. The fragment will take care of attaching the root automatically.
86 val domainSettingsView = inflater.inflate(R.layout.domain_settings_fragment, container, false)
88 // Get the current theme status.
89 val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
91 // Get a handle for the shared preference.
92 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
94 // Store the default settings.
95 val defaultUserAgentName = sharedPreferences.getString(getString(R.string.user_agent_key), getString(R.string.user_agent_default_value))
96 val defaultCustomUserAgentString = sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value))
97 val defaultXRequestedWithHeader = sharedPreferences.getBoolean(getString(R.string.x_requested_with_header_key), true)
98 val defaultFontSizeString = sharedPreferences.getString(getString(R.string.font_size_key), getString(R.string.font_size_default_value))
99 val defaultSwipeToRefresh = sharedPreferences.getBoolean(getString(R.string.swipe_to_refresh_key), true)
100 val defaultWebViewTheme = sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value))
101 val defaultWideViewport = sharedPreferences.getBoolean(getString(R.string.wide_viewport_key), true)
102 val defaultDisplayWebpageImages = sharedPreferences.getBoolean(getString(R.string.display_webpage_images_key), true)
104 // Get handles for the views.
105 val domainSettingsScrollView = domainSettingsView.findViewById<ScrollView>(R.id.domain_settings_scrollview)
106 val domainNameEditText = domainSettingsView.findViewById<EditText>(R.id.domain_settings_name_edittext)
107 val javaScriptImageView = domainSettingsView.findViewById<ImageView>(R.id.javascript_imageview)
108 val javaScriptSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.javascript_switch)
109 val cookiesImageView = domainSettingsView.findViewById<ImageView>(R.id.cookies_imageview)
110 val cookiesSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.cookies_switch)
111 val domStorageImageView = domainSettingsView.findViewById<ImageView>(R.id.dom_storage_imageview)
112 val domStorageSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.dom_storage_switch)
113 val formDataImageView = domainSettingsView.findViewById<ImageView>(R.id.form_data_imageview) // The form data views can be remove once the minimum API >= 26.
114 val formDataSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.form_data_switch) // The form data views can be remove once the minimum API >= 26.
115 val easyListImageView = domainSettingsView.findViewById<ImageView>(R.id.easylist_imageview)
116 val easyListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.easylist_switch)
117 val easyPrivacyImageView = domainSettingsView.findViewById<ImageView>(R.id.easyprivacy_imageview)
118 val easyPrivacySwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.easyprivacy_switch)
119 val fanboysAnnoyanceListImageView = domainSettingsView.findViewById<ImageView>(R.id.fanboys_annoyance_list_imageview)
120 val fanboysAnnoyanceListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.fanboys_annoyance_list_switch)
121 val fanboysSocialBlockingListImageView = domainSettingsView.findViewById<ImageView>(R.id.fanboys_social_blocking_list_imageview)
122 val fanboysSocialBlockingListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.fanboys_social_blocking_list_switch)
123 val ultraListImageView = domainSettingsView.findViewById<ImageView>(R.id.ultralist_imageview)
124 val ultraListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.ultralist_switch)
125 val ultraPrivacyImageView = domainSettingsView.findViewById<ImageView>(R.id.ultraprivacy_imageview)
126 val ultraPrivacySwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.ultraprivacy_switch)
127 val blockAllThirdPartyRequestsImageView = domainSettingsView.findViewById<ImageView>(R.id.block_all_third_party_requests_imageview)
128 val blockAllThirdPartyRequestsSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.block_all_third_party_requests_switch)
129 val userAgentSpinner = domainSettingsView.findViewById<Spinner>(R.id.user_agent_spinner)
130 val userAgentTextView = domainSettingsView.findViewById<TextView>(R.id.user_agent_textview)
131 val customUserAgentEditText = domainSettingsView.findViewById<EditText>(R.id.custom_user_agent_edittext)
132 val xRequestedWithHeaderImageView = domainSettingsView.findViewById<ImageView>(R.id.x_requested_with_header_imageview)
133 val xRequestedWithHeaderSpinner = domainSettingsView.findViewById<Spinner>(R.id.x_requested_with_header_spinner)
134 val xRequestedWithHeaderTextView = domainSettingsView.findViewById<TextView>(R.id.x_requested_with_header_textview)
135 val xRequestedWithHeaderExplanationTextView = domainSettingsView.findViewById<TextView>(R.id.x_requested_with_header_explanation_textview)
136 val fontSizeSpinner = domainSettingsView.findViewById<Spinner>(R.id.font_size_spinner)
137 val defaultFontSizeTextView = domainSettingsView.findViewById<TextView>(R.id.default_font_size_textview)
138 val customFontSizeEditText = domainSettingsView.findViewById<EditText>(R.id.custom_font_size_edittext)
139 val swipeToRefreshImageView = domainSettingsView.findViewById<ImageView>(R.id.swipe_to_refresh_imageview)
140 val swipeToRefreshSpinner = domainSettingsView.findViewById<Spinner>(R.id.swipe_to_refresh_spinner)
141 val swipeToRefreshTextView = domainSettingsView.findViewById<TextView>(R.id.swipe_to_refresh_textview)
142 val webViewThemeImageView = domainSettingsView.findViewById<ImageView>(R.id.webview_theme_imageview)
143 val webViewThemeSpinner = domainSettingsView.findViewById<Spinner>(R.id.webview_theme_spinner)
144 val webViewThemeTextView = domainSettingsView.findViewById<TextView>(R.id.webview_theme_textview)
145 val wideViewportImageView = domainSettingsView.findViewById<ImageView>(R.id.wide_viewport_imageview)
146 val wideViewportSpinner = domainSettingsView.findViewById<Spinner>(R.id.wide_viewport_spinner)
147 val wideViewportTextView = domainSettingsView.findViewById<TextView>(R.id.wide_viewport_textview)
148 val displayWebpageImagesImageView = domainSettingsView.findViewById<ImageView>(R.id.display_webpage_images_imageview)
149 val displayWebpageImagesSpinner = domainSettingsView.findViewById<Spinner>(R.id.display_webpage_images_spinner)
150 val displayImagesTextView = domainSettingsView.findViewById<TextView>(R.id.display_webpage_images_textview)
151 val pinnedSslCertificateImageView = domainSettingsView.findViewById<ImageView>(R.id.pinned_ssl_certificate_imageview)
152 val pinnedSslCertificateSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.pinned_ssl_certificate_switch)
153 val savedSslCardView = domainSettingsView.findViewById<CardView>(R.id.saved_ssl_certificate_cardview)
154 val savedSslCertificateLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.saved_ssl_certificate_linearlayout)
155 val savedSslCertificateRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.saved_ssl_certificate_radiobutton)
156 val savedSslIssuedToCNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_cname)
157 val savedSslIssuedToONameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_oname)
158 val savedSslIssuedToUNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_uname)
159 val savedSslIssuedByCNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_cname)
160 val savedSslIssuedByONameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_oname)
161 val savedSslIssuedByUNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_uname)
162 val savedSslStartDateTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_start_date)
163 val savedSslEndDateTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_end_date)
164 val currentSslCardView = domainSettingsView.findViewById<CardView>(R.id.current_website_certificate_cardview)
165 val currentWebsiteCertificateLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.current_website_certificate_linearlayout)
166 val currentWebsiteCertificateRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.current_website_certificate_radiobutton)
167 val currentSslIssuedToCNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_cname)
168 val currentSslIssuedToONameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_oname)
169 val currentSslIssuedToUNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_uname)
170 val currentSslIssuedByCNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_cname)
171 val currentSslIssuedByONameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_oname)
172 val currentSslIssuedByUNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_uname)
173 val currentSslStartDateTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_start_date)
174 val currentSslEndDateTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_end_date)
175 val noCurrentWebsiteCertificateTextView = domainSettingsView.findViewById<TextView>(R.id.no_current_website_certificate)
176 val pinnedIpAddressesImageView = domainSettingsView.findViewById<ImageView>(R.id.pinned_ip_addresses_imageview)
177 val pinnedIpAddressesSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.pinned_ip_addresses_switch)
178 val savedIpAddressesCardView = domainSettingsView.findViewById<CardView>(R.id.saved_ip_addresses_cardview)
179 val savedIpAddressesLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.saved_ip_addresses_linearlayout)
180 val savedIpAddressesRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.saved_ip_addresses_radiobutton)
181 val savedIpAddressesTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ip_addresses_textview)
182 val currentIpAddressesCardView = domainSettingsView.findViewById<CardView>(R.id.current_ip_addresses_cardview)
183 val currentIpAddressesLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.current_ip_addresses_linearlayout)
184 val currentIpAddressesRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.current_ip_addresses_radiobutton)
185 val currentIpAddressesTextView = domainSettingsView.findViewById<TextView>(R.id.current_ip_addresses_textview)
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 xRequestedWithHeaderInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.X_REQUESTED_WITH_HEADER))
218 val fontSizeInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE))
219 val swipeToRefreshInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH))
220 val webViewThemeInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME))
221 val wideViewportInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WIDE_VIEWPORT))
222 val displayImagesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DISPLAY_IMAGES))
223 val pinnedSslCertificateInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE))
224 val savedSslIssuedToCNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME))
225 val savedSslIssuedToONameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION))
226 val savedSslIssuedToUNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT))
227 val savedSslIssuedByCNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME))
228 val savedSslIssuedByONameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION))
229 val savedSslIssuedByUNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT))
230 val pinnedIpAddressesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_IP_ADDRESSES))
231 val savedIpAddresses = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.IP_ADDRESSES))
233 // Get the SSL dates from the database.
234 val savedSslStartDateLong = domainCursor.getLong(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_START_DATE))
235 val savedSslEndDateLong = domainCursor.getLong(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_END_DATE))
237 // Initialize the saved SSL certificate date variables.
238 var savedSslStartDate: Date? = null
239 var savedSslEndDate: Date? = null
241 // Only get the saved SSL certificate dates from the cursor if they are not set to `0`.
242 if (savedSslStartDateLong != 0L)
243 savedSslStartDate = Date(savedSslStartDateLong)
244 if (savedSslEndDateLong != 0L)
245 savedSslEndDate = Date(savedSslEndDateLong)
247 // Create array adapters for the spinners.
248 val translatedUserAgentArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.translated_domain_settings_user_agent_names, R.layout.spinner_item)
249 val xRequestedWithHeaderArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.x_requested_with_header_array, R.layout.spinner_item)
250 val fontSizeArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.font_size_array, R.layout.spinner_item)
251 val swipeToRefreshArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.swipe_to_refresh_array, R.layout.spinner_item)
252 val webViewThemeArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.webview_theme_array, R.layout.spinner_item)
253 val wideViewportArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.wide_viewport_array, R.layout.spinner_item)
254 val displayImagesArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.display_webpage_images_array, R.layout.spinner_item)
256 // Set the drop down view resource on the spinners.
257 translatedUserAgentArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
258 xRequestedWithHeaderArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
259 fontSizeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
260 swipeToRefreshArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
261 webViewThemeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
262 wideViewportArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
263 displayImagesArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
265 // Set the array adapters for the spinners.
266 userAgentSpinner.adapter = translatedUserAgentArrayAdapter
267 xRequestedWithHeaderSpinner.adapter = xRequestedWithHeaderArrayAdapter
268 fontSizeSpinner.adapter = fontSizeArrayAdapter
269 swipeToRefreshSpinner.adapter = swipeToRefreshArrayAdapter
270 webViewThemeSpinner.adapter = webViewThemeArrayAdapter
271 wideViewportSpinner.adapter = wideViewportArrayAdapter
272 displayWebpageImagesSpinner.adapter = displayImagesArrayAdapter
274 // Create a spannable string builder for each TextView that needs multiple colors of text.
275 val savedSslIssuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString)
276 val savedSslIssuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + savedSslIssuedToONameString)
277 val savedSslIssuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + savedSslIssuedToUNameString)
278 val savedSslIssuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedByCNameString)
279 val savedSslIssuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + savedSslIssuedByONameString)
280 val savedSslIssuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + savedSslIssuedByUNameString)
282 // Create the date spannable string builders.
283 val savedSslStartDateStringBuilder: SpannableStringBuilder = if (savedSslStartDate == null)
284 SpannableStringBuilder(startDateLabel)
286 SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslStartDate))
288 val savedSslEndDateStringBuilder: SpannableStringBuilder = if (savedSslEndDate == null)
289 SpannableStringBuilder(endDateLabel)
291 SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslEndDate))
293 // Create the color spans.
294 val blueColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.alt_blue_text))
295 val redColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.red_text))
297 // Set the domain name from the the database cursor.
298 domainNameEditText.setText(domainNameString)
300 // Update the certificates' Common Name color when the domain name text changes.
301 domainNameEditText.addTextChangedListener(object : TextWatcher {
302 override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
306 override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
310 override fun afterTextChanged(s: Editable) {
311 // Get the new domain name.
312 val newDomainName = domainNameEditText.text.toString()
314 // Check the saved SSL certificate against the new domain name.
315 val savedSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, savedSslIssuedToCNameString)
317 // Create a spannable string builder for the saved certificate's Common Name.
318 val savedSslCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString)
320 // Format the saved certificate's Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
321 if (savedSslMatchesNewDomainName) {
322 savedSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
324 savedSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, savedSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
327 // Update the saved SSL issued to CName text view.
328 savedSslIssuedToCNameTextView.text = savedSslCNameStringBuilder
330 // Update the current website certificate if it exists.
331 if (DomainsActivity.sslIssuedToCName != null) {
332 // Check the current website certificate against the new domain name.
333 val currentSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, DomainsActivity.sslIssuedToCName)
335 // Create a spannable string builder for the current website certificate's Common Name.
336 val currentSslCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName)
338 // Format the current certificate Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
339 if (currentSslMatchesNewDomainName) {
340 currentSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
342 currentSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, currentSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
345 // Update the current SSL issued to CName text view.
346 currentSslIssuedToCNameTextView.text = currentSslCNameStringBuilder
351 // Set the switch positions.
352 javaScriptSwitch.isChecked = javaScriptInt == 1
353 cookiesSwitch.isChecked = cookiesInt == 1
354 domStorageSwitch.isChecked = domStorageInt == 1
355 formDataSwitch.isChecked = formDataInt == 1 // Form data can be removed once the minimum API >= 26.
356 easyListSwitch.isChecked = easyListInt == 1
357 easyPrivacySwitch.isChecked = easyPrivacyInt == 1
358 fanboysAnnoyanceListSwitch.isChecked = fanboysAnnoyanceListInt == 1
359 fanboysSocialBlockingListSwitch.isChecked = fanboysSocialBlockingListInt == 1
360 ultraListSwitch.isChecked = ultraListInt == 1
361 ultraPrivacySwitch.isChecked = ultraPrivacyInt == 1
362 blockAllThirdPartyRequestsSwitch.isChecked = blockAllThirdPartyRequestsInt == 1
363 pinnedSslCertificateSwitch.isChecked = pinnedSslCertificateInt == 1
364 pinnedIpAddressesSwitch.isChecked = pinnedIpAddressesInt == 1
366 // Set the switch icon colors.
367 cookiesImageView.isSelected = cookiesInt == 1
368 domStorageImageView.isSelected = domStorageInt == 1
369 formDataImageView.isSelected = formDataInt == 1 // Form data can be removed once the minimum API >= 26.
370 easyListImageView.isSelected = easyListInt == 1
371 easyPrivacyImageView.isSelected = easyPrivacyInt == 1
372 fanboysAnnoyanceListImageView.isSelected = fanboysAnnoyanceListInt == 1
373 fanboysSocialBlockingListImageView.isSelected = fanboysSocialBlockingListInt == 1
374 ultraListImageView.isSelected = ultraListInt == 1
375 ultraPrivacyImageView.isSelected = ultraPrivacyInt == 1
376 blockAllThirdPartyRequestsImageView.isSelected = blockAllThirdPartyRequestsInt == 1
377 pinnedSslCertificateImageView.isSelected = pinnedSslCertificateInt == 1
378 pinnedIpAddressesImageView.isSelected = pinnedIpAddressesInt == 1
380 // Set the JavaScript icon.
381 if (javaScriptInt == 1)
382 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null))
384 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null))
386 // Set the DOM storage switch status based on the JavaScript status.
387 domStorageSwitch.isEnabled = (javaScriptInt == 1)
389 // Set the DOM storage icon ghosted status based on the JavaScript status.
390 domStorageImageView.isEnabled = (javaScriptInt == 1)
392 // Set the form data visibility. Form data can be removed once the minimum API >= 26.
393 if (Build.VERSION.SDK_INT >= 26) {
394 // Hide the form data image view and switch.
395 formDataImageView.visibility = View.GONE
396 formDataSwitch.visibility = View.GONE
399 // Set Fanboy's Social Blocking List switch status based on the Annoyance List status.
400 fanboysSocialBlockingListSwitch.isEnabled = (fanboysAnnoyanceListInt == 0)
402 // Set the Social Blocking List icon ghosted status based on the Annoyance List status.
403 fanboysSocialBlockingListImageView.isEnabled = (fanboysAnnoyanceListInt == 0)
405 // Inflated a WebView to get the default user agent.
406 // `@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.
407 @SuppressLint("InflateParams") val bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false)
408 val bareWebView = bareWebViewLayout.findViewById<WebView>(R.id.bare_webview)
409 val webViewDefaultUserAgentString = bareWebView.settings.userAgentString
411 // Get a handle for the user agent array adapter. This array does not contain the `System default` entry.
412 val userAgentNamesArray = ArrayAdapter.createFromResource(requireContext(), R.array.user_agent_names, R.layout.spinner_item)
414 // Get the positions of the user agent and the default user agent.
415 val userAgentArrayPosition = userAgentNamesArray.getPosition(currentUserAgentName)
416 val defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName)
418 // Get a handle for the user agent data array. This array does not contain the `System default` entry.
419 val userAgentDataArray = resources.getStringArray(R.array.user_agent_data)
421 // Set the user agent text.
422 if (currentUserAgentName == getString(R.string.system_default_user_agent)) { // Use the system default user agent.
423 // Set the user agent according to the system default.
424 when (defaultUserAgentArrayPosition) {
425 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
426 MainWebViewActivity.UNRECOGNIZED_USER_AGENT -> userAgentTextView.text = defaultUserAgentName
428 // Display the WebView default user agent.
429 MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentTextView.text = webViewDefaultUserAgentString
431 // Display the custom user agent.
432 MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT -> userAgentTextView.text = defaultCustomUserAgentString
434 // Get the user agent string from the user agent data array.
435 else -> userAgentTextView.text = userAgentDataArray[defaultUserAgentArrayPosition]
437 } else if (userAgentArrayPosition == MainWebViewActivity.UNRECOGNIZED_USER_AGENT || currentUserAgentName == getString(R.string.custom_user_agent)) {
438 // 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.
439 // Set the user agent spinner to `Custom user agent`.
440 userAgentSpinner.setSelection(MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT)
442 // Hide the user agent text view.
443 userAgentTextView.visibility = View.GONE
445 // Show the custom user agent edit text and set the current user agent name as the text.
446 customUserAgentEditText.visibility = View.VISIBLE
447 customUserAgentEditText.setText(currentUserAgentName)
448 } else { // The user agent name contains one of the canonical user agents.
449 // 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.
450 userAgentSpinner.setSelection(userAgentArrayPosition + 1)
452 // Show the user agent text view.
453 userAgentTextView.visibility = View.VISIBLE
455 // Hide the custom user agent edit text.
456 customUserAgentEditText.visibility = View.GONE
458 // Set the user agent text.
459 if (userAgentArrayPosition == MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT) { // The WebView default user agent is selected.
460 // Display the WebView default user agent.
461 userAgentTextView.text = webViewDefaultUserAgentString
462 } else { // A user agent besides the default is selected.
463 // 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.
464 userAgentTextView.text = userAgentDataArray[userAgentArrayPosition + 1]
468 // Open the user agent spinner when the text view is clicked.
469 userAgentTextView.setOnClickListener { userAgentSpinner.performClick() }
471 // Select the X-Requested-With header selection in the spinner.
472 xRequestedWithHeaderSpinner.setSelection(xRequestedWithHeaderInt)
474 // Set the X-Requested-With header text.
475 if (defaultXRequestedWithHeader)
476 xRequestedWithHeaderTextView.text = xRequestedWithHeaderArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
478 xRequestedWithHeaderTextView.text = xRequestedWithHeaderArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
480 // Set the X-Requested-With header icon and text view settings.
481 when (xRequestedWithHeaderInt) {
482 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
483 // Set the icon color.
484 xRequestedWithHeaderImageView.isSelected = defaultXRequestedWithHeader
486 // Show the X-Requested-With header text view.
487 xRequestedWithHeaderTextView.visibility = View.VISIBLE
490 DomainsDatabaseHelper.ENABLED -> {
491 // Set the icon color.
492 xRequestedWithHeaderImageView.isSelected = true
494 // Hide the X-Requested-With header text view.
495 xRequestedWithHeaderTextView.visibility = View.GONE
498 DomainsDatabaseHelper.DISABLED -> {
499 // Set the icon color.
500 xRequestedWithHeaderImageView.isSelected = false
502 // Hide the X-Requested-With header text view.
503 xRequestedWithHeaderTextView.visibility = View.GONE
507 // Open the X-Requested-With header spinner when the text view is clicked.
508 xRequestedWithHeaderTextView.setOnClickListener { xRequestedWithHeaderSpinner.performClick() }
510 // Open the X-Requested-With header spinner when the explanation text view is clicked.
511 xRequestedWithHeaderExplanationTextView.setOnClickListener { xRequestedWithHeaderSpinner.performClick() }
513 // Display the font size settings.
514 if (fontSizeInt == 0) { // `0` is the code for system default font size.
515 // Set the font size to the system default.
516 fontSizeSpinner.setSelection(0)
518 // Show the default font size text view.
519 defaultFontSizeTextView.visibility = View.VISIBLE
521 // Hide the custom font size edit text.
522 customFontSizeEditText.visibility = View.GONE
524 // 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.
525 customFontSizeEditText.setText(defaultFontSizeString)
526 } else { // A custom font size is selected.
527 // Set the spinner to the custom font size.
528 fontSizeSpinner.setSelection(1)
530 // Hide the default font size text view.
531 defaultFontSizeTextView.visibility = View.GONE
533 // Show the custom font size edit text.
534 customFontSizeEditText.visibility = View.GONE
536 // Set the custom font size.
537 customFontSizeEditText.setText(fontSizeInt.toString())
540 // Initialize the default font size percentage string.
541 val defaultFontSizePercentageString = "$defaultFontSizeString%"
543 // Set the default font size text in the text view.
544 defaultFontSizeTextView.text = defaultFontSizePercentageString
546 // Open the font size spinner when the text view is clicked.
547 defaultFontSizeTextView.setOnClickListener { fontSizeSpinner.performClick() }
549 // Select the swipe-to-refresh selection in the spinner.
550 swipeToRefreshSpinner.setSelection(swipeToRefreshInt)
552 // Set the swipe-to-refresh text.
553 if (defaultSwipeToRefresh)
554 swipeToRefreshTextView.text = swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
556 swipeToRefreshTextView.text = swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
558 // Set the swipe-to-refresh icon and text view settings.
559 when (swipeToRefreshInt) {
560 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
561 // Set the icon color.
562 swipeToRefreshImageView.isSelected = defaultSwipeToRefresh
564 // Show the swipe-to-refresh text view.
565 swipeToRefreshTextView.visibility = View.VISIBLE
568 DomainsDatabaseHelper.ENABLED -> {
569 // Set the icon color.
570 swipeToRefreshImageView.isSelected = true
572 // Hide the swipe-to-refresh text view.
573 swipeToRefreshTextView.visibility = View.GONE
576 DomainsDatabaseHelper.DISABLED -> {
577 // Set the icon color.
578 swipeToRefreshImageView.isSelected = false
580 // Hide the swipe-to-refresh text view.
581 swipeToRefreshTextView.visibility = View.GONE
585 // Open the swipe-to-refresh spinner when the text view is clicked.
586 swipeToRefreshTextView.setOnClickListener { swipeToRefreshSpinner.performClick() }
588 // Get the WebView theme string arrays.
589 val webViewThemeStringArray = resources.getStringArray(R.array.webview_theme_array)
590 val webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values)
592 // Get the WebView theme entry number that matches the current WebView theme.
593 val appWebViewThemeEntryNumber = when (defaultWebViewTheme) {
594 webViewThemeEntryValuesStringArray[1] -> { 1 } // The light theme is selected.
595 webViewThemeEntryValuesStringArray[2] -> { 2 } // The dark theme is selected.
596 else -> { 0 } // The system default theme is selected.
599 // Select the WebView theme in the spinner.
600 webViewThemeSpinner.setSelection(webViewThemeInt)
602 // Set the WebView theme text.
603 if (appWebViewThemeEntryNumber == DomainsDatabaseHelper.SYSTEM_DEFAULT) { // The app WebView theme is system default.
604 // Set the text according to the current UI theme.
605 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
606 webViewThemeTextView.text = webViewThemeStringArray[DomainsDatabaseHelper.LIGHT_THEME]
608 webViewThemeTextView.text = webViewThemeStringArray[DomainsDatabaseHelper.DARK_THEME]
609 } else { // The app WebView theme is not system default.
610 // Set the text according to the app WebView theme.
611 webViewThemeTextView.text = webViewThemeStringArray[appWebViewThemeEntryNumber]
614 // Set the WebView theme icon and text visibility.
615 when (webViewThemeInt) {
616 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
617 // Set the icon color.
618 when (appWebViewThemeEntryNumber) {
619 DomainsDatabaseHelper.SYSTEM_DEFAULT -> webViewThemeImageView.isSelected = (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
620 DomainsDatabaseHelper.LIGHT_THEME -> webViewThemeImageView.isSelected = true
621 DomainsDatabaseHelper.DARK_THEME -> webViewThemeImageView.isSelected = false
624 // Show the WebView theme text view.
625 webViewThemeTextView.visibility = View.VISIBLE
628 DomainsDatabaseHelper.LIGHT_THEME -> {
629 // Set the icon color.
630 webViewThemeImageView.isSelected = true
632 // Hide the WebView theme text view.
633 webViewThemeTextView.visibility = View.GONE
636 DomainsDatabaseHelper.DARK_THEME -> {
637 // Set the icon color.
638 webViewThemeImageView.isSelected = false
640 // Hide the WebView theme text view.
641 webViewThemeTextView.visibility = View.GONE
645 // Open the WebView theme spinner when the text view is clicked.
646 webViewThemeTextView.setOnClickListener { webViewThemeSpinner.performClick() }
648 // Select the wide viewport in the spinner.
649 wideViewportSpinner.setSelection(wideViewportInt)
651 // Set the default wide viewport text.
652 if (defaultWideViewport)
653 wideViewportTextView.text = wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
654 else wideViewportTextView.text = wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
656 // Set the wide viewport icon and text view settings.
657 when (wideViewportInt) {
658 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
659 // Set the icon color.
660 wideViewportImageView.isSelected = defaultWideViewport
662 // Show the wide viewport text view.
663 wideViewportTextView.visibility = View.VISIBLE
666 DomainsDatabaseHelper.ENABLED -> {
667 // Set the icon color.
668 wideViewportImageView.isSelected = true
670 // Hide the wide viewport text view.
671 wideViewportTextView.visibility = View.GONE
674 DomainsDatabaseHelper.DISABLED -> {
675 // Set the icon color.
676 wideViewportImageView.isSelected = false
678 // Hide the wide viewport text view.
679 wideViewportTextView.visibility = View.GONE
683 // Open the wide viewport spinner when the text view is clicked.
684 wideViewportTextView.setOnClickListener { wideViewportSpinner.performClick() }
686 // Display the website images mode in the spinner.
687 displayWebpageImagesSpinner.setSelection(displayImagesInt)
689 // Set the default display images text.
690 if (defaultDisplayWebpageImages)
691 displayImagesTextView.text = displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
693 displayImagesTextView.text = displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
695 // Set the display website images icon and text view settings.
696 when (displayImagesInt) {
697 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
698 // Set the icon color.
699 displayWebpageImagesImageView.isSelected = defaultDisplayWebpageImages
701 // Show the display images text view.
702 displayImagesTextView.visibility = View.VISIBLE
705 DomainsDatabaseHelper.ENABLED -> {
706 // Set the icon color.
707 displayWebpageImagesImageView.isSelected = true
709 // Hide the display images text view.
710 displayImagesTextView.visibility = View.GONE
713 DomainsDatabaseHelper.DISABLED -> {
714 // Set the icon color.
715 displayWebpageImagesImageView.isSelected = false
717 // Hide the display images text view.
718 displayImagesTextView.visibility = View.GONE
722 // Open the display images spinner when the text view is clicked.
723 displayImagesTextView.setOnClickListener { displayWebpageImagesSpinner.performClick() }
725 // Store the current date.
726 val currentDate = Calendar.getInstance().time
728 // Setup the string builders to display the general certificate information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
729 savedSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, savedSslIssuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
730 savedSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, savedSslIssuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
731 savedSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslIssuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
732 savedSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, savedSslIssuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
733 savedSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, savedSslIssuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
735 // Check the certificate Common Name against the domain name.
736 val savedSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslIssuedToCNameString)
738 // Format the issued to Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
739 if (savedSslCommonNameMatchesDomainName)
740 savedSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
742 savedSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, savedSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
744 // Format the start date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
745 if (savedSslStartDate != null && savedSslStartDate.after(currentDate)) // The certificate start date is in the future.
746 savedSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, savedSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
747 else // The certificate start date is in the past.
748 savedSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, savedSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
750 // Format the end date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
751 if (savedSslEndDate != null && savedSslEndDate.before(currentDate)) // The certificate end date is in the past.
752 savedSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, savedSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
753 else // The certificate end date is in the future.
754 savedSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, savedSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
756 // Display the saved website SSL certificate strings.
757 savedSslIssuedToCNameTextView.text = savedSslIssuedToCNameStringBuilder
758 savedSslIssuedToONameTextView.text = savedSslIssuedToONameStringBuilder
759 savedSslIssuedToUNameTextView.text = savedSslIssuedToUNameStringBuilder
760 savedSslIssuedByCNameTextView.text = savedSslIssuedByCNameStringBuilder
761 savedSslIssuedByONameTextView.text = savedSslIssuedByONameStringBuilder
762 savedSslIssuedByUNameTextView.text = savedSslIssuedByUNameStringBuilder
763 savedSslStartDateTextView.text = savedSslStartDateStringBuilder
764 savedSslEndDateTextView.text = savedSslEndDateStringBuilder
766 // Populate the current website SSL certificate if there is one.
767 if (DomainsActivity.sslIssuedToCName != null) {
768 // Get dates from the raw long values.
769 val currentSslStartDate = Date(DomainsActivity.sslStartDateLong)
770 val currentSslEndDate = Date(DomainsActivity.sslEndDateLong)
772 // Create a spannable string builder for each text view that needs multiple colors of text.
773 val currentSslIssuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName)
774 val currentSslIssuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedToOName)
775 val currentSslIssuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedToUName)
776 val currentSslIssuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedByCName)
777 val currentSslIssuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedByOName)
778 val currentSslIssuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedByUName)
779 val currentSslStartDateStringBuilder = SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslStartDate))
780 val currentSslEndDateStringBuilder = SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslEndDate))
782 // Setup the string builders to display the general certificate information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
783 currentSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, currentSslIssuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
784 currentSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, currentSslIssuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
785 currentSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslIssuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
786 currentSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, currentSslIssuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
787 currentSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, currentSslIssuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
789 // Check the certificate Common Name against the domain name.
790 val currentSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, DomainsActivity.sslIssuedToCName)
792 // Format the issued to Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
793 if (currentSslCommonNameMatchesDomainName)
794 currentSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
796 currentSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, currentSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
798 // Format the start date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
799 if (currentSslStartDate.after(currentDate)) // The certificate start date is in the future.
800 currentSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, currentSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
801 else // The certificate start date is in the past.
802 currentSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, currentSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
804 // Format the end date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
805 if (currentSslEndDate.before(currentDate)) // The certificate end date is in the past.
806 currentSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, currentSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
807 else // The certificate end date is in the future.
808 currentSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, currentSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
810 // Display the current website SSL certificate strings.
811 currentSslIssuedToCNameTextView.text = currentSslIssuedToCNameStringBuilder
812 currentSslIssuedToONameTextView.text = currentSslIssuedToONameStringBuilder
813 currentSslIssuedToUNameTextView.text = currentSslIssuedToUNameStringBuilder
814 currentSslIssuedByCNameTextView.text = currentSslIssuedByCNameStringBuilder
815 currentSslIssuedByONameTextView.text = currentSslIssuedByONameStringBuilder
816 currentSslIssuedByUNameTextView.text = currentSslIssuedByUNameStringBuilder
817 currentSslStartDateTextView.text = currentSslStartDateStringBuilder
818 currentSslEndDateTextView.text = currentSslEndDateStringBuilder
821 // Set the initial display status of the SSL certificates card views.
822 if (pinnedSslCertificateSwitch.isChecked) { // An SSL certificate is pinned.
823 // Set the visibility of the saved SSL certificate.
824 if (savedSslIssuedToCNameString == null)
825 savedSslCardView.visibility = View.GONE
827 savedSslCardView.visibility = View.VISIBLE
829 // Set the visibility of the current website SSL certificate.
830 if (DomainsActivity.sslIssuedToCName == null) { // There is no current SSL certificate.
831 // Hide the SSL certificate.
832 currentSslCardView.visibility = View.GONE
834 // Show the instruction.
835 noCurrentWebsiteCertificateTextView.visibility = View.VISIBLE
836 } else { // There is a current SSL certificate.
837 // Show the SSL certificate.
838 currentSslCardView.visibility = View.VISIBLE
840 // Hide the instruction.
841 noCurrentWebsiteCertificateTextView.visibility = View.GONE
844 // Set the status of the radio buttons and the card view backgrounds.
845 if (savedSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is displayed.
846 // Check the saved SSL certificate radio button.
847 savedSslCertificateRadioButton.isChecked = true
849 // Uncheck the current website SSL certificate radio button.
850 currentWebsiteCertificateRadioButton.isChecked = false
852 // Darken the background of the current website SSL certificate linear layout.
853 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
854 } else if (currentSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is hidden but the current website SSL certificate is visible.
855 // Check the current website SSL certificate radio button.
856 currentWebsiteCertificateRadioButton.isChecked = true
858 // Uncheck the saved SSL certificate radio button.
859 savedSslCertificateRadioButton.isChecked = false
860 } else { // Neither SSL certificate is visible.
861 // Uncheck both radio buttons.
862 savedSslCertificateRadioButton.isChecked = false
863 currentWebsiteCertificateRadioButton.isChecked = false
865 } else { // An SSL certificate is not pinned.
866 // Hide the SSl certificates and instructions.
867 savedSslCardView.visibility = View.GONE
868 currentSslCardView.visibility = View.GONE
869 noCurrentWebsiteCertificateTextView.visibility = View.GONE
871 // Uncheck the radio buttons.
872 savedSslCertificateRadioButton.isChecked = false
873 currentWebsiteCertificateRadioButton.isChecked = false
876 // Populate the saved and current IP addresses.
877 savedIpAddressesTextView.text = savedIpAddresses
878 currentIpAddressesTextView.text = DomainsActivity.currentIpAddresses
880 // Set the initial display status of the IP addresses card views.
881 if (pinnedIpAddressesSwitch.isChecked) { // IP addresses are pinned.
882 // Set the visibility of the saved IP addresses.
883 if (savedIpAddresses == null) // There are no saved IP addresses.
884 savedIpAddressesCardView.visibility = View.GONE
885 else // There are saved IP addresses.
886 savedIpAddressesCardView.visibility = View.VISIBLE
888 // Set the visibility of the current IP addresses.
889 currentIpAddressesCardView.visibility = View.VISIBLE
891 // Set the status of the radio buttons and the card view backgrounds.
892 if (savedIpAddressesCardView.visibility == View.VISIBLE) { // The saved IP addresses are displayed.
893 // Check the saved IP addresses radio button.
894 savedIpAddressesRadioButton.isChecked = true
896 // Uncheck the current IP addresses radio button.
897 currentIpAddressesRadioButton.isChecked = false
899 // Darken the background of the current IP addresses linear layout.
900 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
901 } else { // The saved IP addresses are hidden.
902 // Check the current IP addresses radio button.
903 currentIpAddressesRadioButton.isChecked = true
905 // Uncheck the saved IP addresses radio button.
906 savedIpAddressesRadioButton.isChecked = false
908 } else { // IP addresses are not pinned.
909 // Hide the IP addresses card views.
910 savedIpAddressesCardView.visibility = View.GONE
911 currentIpAddressesCardView.visibility = View.GONE
913 // Uncheck the radio buttons.
914 savedIpAddressesRadioButton.isChecked = false
915 currentIpAddressesRadioButton.isChecked = false
919 // Set the JavaScript switch listener.
920 javaScriptSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
921 // Update the JavaScript icon.
923 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null))
925 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null))
927 // Set the DOM storage switch status.
928 domStorageSwitch.isEnabled = isChecked
930 // Set the DOM storage ghosted icon status.
931 domStorageImageView.isEnabled = isChecked
934 // Set the cookies switch listener.
935 cookiesSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
936 // Update the icon color.
937 cookiesImageView.isSelected = isChecked
940 // Set the DOM Storage switch listener.
941 domStorageSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
942 // Update the icon color.
943 domStorageImageView.isSelected = isChecked
946 // Set the form data switch listener. It can be removed once the minimum API >= 26.
947 if (Build.VERSION.SDK_INT < 26) {
948 formDataSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
949 // Update the icon color.
950 formDataImageView.isSelected = isChecked
954 // Set the EasyList switch listener.
955 easyListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
956 // Update the icon color.
957 easyListImageView.isSelected = isChecked
960 // Set the EasyPrivacy switch listener.
961 easyPrivacySwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
962 // Update the icon color.
963 easyPrivacyImageView.isSelected = isChecked
966 // Set the Fanboy's Annoyance List switch listener.
967 fanboysAnnoyanceListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
968 // Update the icon color.
969 fanboysAnnoyanceListImageView.isSelected = isChecked
971 // Set Fanboy's Social Blocking List switch position.
972 fanboysSocialBlockingListSwitch.isEnabled = !isChecked
974 // Set the Social Blocking List icon ghosted status.
975 fanboysSocialBlockingListImageView.isEnabled = !isChecked
978 // Set the Fanboy's Social Blocking List switch listener.
979 fanboysSocialBlockingListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
980 // Update the icon color.
981 fanboysSocialBlockingListImageView.isSelected = isChecked
984 // Set the UltraList switch listener.
985 ultraListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
986 // Update the icon color.
987 ultraListImageView.isSelected = isChecked
990 // Set the UltraPrivacy switch listener.
991 ultraPrivacySwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
992 // Update the icon color.
993 ultraPrivacyImageView.isSelected = isChecked
996 // Set the block all third-party requests switch listener.
997 blockAllThirdPartyRequestsSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
998 // Update the icon color.
999 blockAllThirdPartyRequestsImageView.isSelected = isChecked
1002 // Set the user agent spinner listener.
1003 userAgentSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1004 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1005 // Set the new user agent.
1007 MainWebViewActivity.DOMAINS_SYSTEM_DEFAULT_USER_AGENT -> {
1008 // Show the user agent text view.
1009 userAgentTextView.visibility = View.VISIBLE
1011 // Hide the custom user agent edit text.
1012 customUserAgentEditText.visibility = View.GONE
1014 // Set the user text.
1015 when (defaultUserAgentArrayPosition) {
1016 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
1017 MainWebViewActivity.UNRECOGNIZED_USER_AGENT -> userAgentTextView.text = defaultUserAgentName
1019 // Display the `WebView` default user agent.
1020 MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentTextView.text = webViewDefaultUserAgentString
1022 // Display the custom user agent.
1023 MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT -> userAgentTextView.text = defaultCustomUserAgentString
1025 // Get the user agent string from the user agent data array.
1026 else -> userAgentTextView.text = userAgentDataArray[defaultUserAgentArrayPosition]
1030 MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT -> {
1031 // Show the user agent text view.
1032 userAgentTextView.visibility = View.VISIBLE
1034 // Set the user agent text.
1035 userAgentTextView.text = webViewDefaultUserAgentString
1037 // Hide the custom user agent EditTex.
1038 customUserAgentEditText.visibility = View.GONE
1041 MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT -> {
1042 // Hide the user agent TextView.
1043 userAgentTextView.visibility = View.GONE
1045 // Show the custom user agent edit text.
1046 customUserAgentEditText.visibility = View.VISIBLE
1048 // Set the current user agent name as the text.
1049 customUserAgentEditText.setText(currentUserAgentName)
1053 // Show the user agent text view.
1054 userAgentTextView.visibility = View.VISIBLE
1056 // Set the text from the user agent data array, which has one less entry than the spinner, so the position must be decremented.
1057 userAgentTextView.text = userAgentDataArray[position - 1]
1059 // Hide the custom user agent edit text.
1060 customUserAgentEditText.visibility = View.GONE
1065 override fun onNothingSelected(parent: AdapterView<*>?) {
1070 // Set the X-Requested-With header spinner listener.
1071 xRequestedWithHeaderSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1072 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1073 // Update the icon and the visibility of the text view.
1075 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1076 // Set the icon color.
1077 xRequestedWithHeaderImageView.isSelected = defaultXRequestedWithHeader
1079 // Show the X-Requested-With header text view.
1080 xRequestedWithHeaderTextView.visibility = View.VISIBLE
1083 DomainsDatabaseHelper.ENABLED -> {
1084 // Set the icon color.
1085 xRequestedWithHeaderImageView.isSelected = true
1087 // Hide the X-Requested-With header text view.
1088 xRequestedWithHeaderTextView.visibility = View.GONE
1091 DomainsDatabaseHelper.DISABLED -> {
1092 // Set the icon color.
1093 xRequestedWithHeaderImageView.isSelected = false
1095 // Hide the X-Requested-With header text view.
1096 xRequestedWithHeaderTextView.visibility = View.GONE
1101 override fun onNothingSelected(parent: AdapterView<*>?) {
1106 // Set the font size spinner listener.
1107 fontSizeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1108 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1109 // Update the font size display options.
1110 if (position == 0) { // The system default font size has been selected.
1111 // Show the default font size text view.
1112 defaultFontSizeTextView.visibility = View.VISIBLE
1114 // Hide the custom font size edit text.
1115 customFontSizeEditText.visibility = View.GONE
1116 } else { // A custom font size has been selected.
1117 // Hide the default font size text view.
1118 defaultFontSizeTextView.visibility = View.GONE
1120 // Show the custom font size edit text.
1121 customFontSizeEditText.visibility = View.VISIBLE
1125 override fun onNothingSelected(parent: AdapterView<*>?) {
1130 // Set the swipe-to-refresh spinner listener.
1131 swipeToRefreshSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1132 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1133 // Update the icon and the visibility of the text view.
1135 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1136 // Set the icon color.
1137 swipeToRefreshImageView.isSelected = defaultSwipeToRefresh
1139 // Show the swipe-to-refresh text view.
1140 swipeToRefreshTextView.visibility = View.VISIBLE
1143 DomainsDatabaseHelper.ENABLED -> {
1144 // Set the icon color.
1145 swipeToRefreshImageView.isSelected = true
1147 // Hide the swipe-to-refresh text view.
1148 swipeToRefreshTextView.visibility = View.GONE
1151 DomainsDatabaseHelper.DISABLED -> {
1152 // Set the icon color.
1153 swipeToRefreshImageView.isSelected = false
1155 // Hide the swipe-to-refresh text view.
1156 swipeToRefreshTextView.visibility = View.GONE
1161 override fun onNothingSelected(parent: AdapterView<*>?) {
1166 // Set the WebView theme spinner listener.
1167 webViewThemeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1168 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1169 // Update the icon and the visibility of the WebView theme text view.
1171 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1172 // Set the icon color.
1173 when (appWebViewThemeEntryNumber) {
1174 DomainsDatabaseHelper.SYSTEM_DEFAULT -> webViewThemeImageView.isSelected = (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
1175 DomainsDatabaseHelper.LIGHT_THEME -> webViewThemeImageView.isSelected = true
1176 DomainsDatabaseHelper.DARK_THEME -> webViewThemeImageView.isSelected = false
1179 // Show the WebView theme text view.
1180 webViewThemeTextView.visibility = View.VISIBLE
1183 DomainsDatabaseHelper.LIGHT_THEME -> {
1184 // Set the icon color.
1185 webViewThemeImageView.isSelected = true
1187 // Hide the WebView theme text view.
1188 webViewThemeTextView.visibility = View.GONE
1191 DomainsDatabaseHelper.DARK_THEME -> {
1192 // Set the icon color.
1193 webViewThemeImageView.isSelected = false
1195 // Hide the WebView theme text view.
1196 webViewThemeTextView.visibility = View.GONE
1201 override fun onNothingSelected(parent: AdapterView<*>?) {
1206 // Set the wide viewport spinner listener.
1207 wideViewportSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1208 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1209 // Update the icon and the visibility of the wide viewport text view.
1211 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1212 // Set the icon color.
1213 wideViewportImageView.isSelected = defaultWideViewport
1215 // Show the wide viewport text view.
1216 wideViewportTextView.visibility = View.VISIBLE
1219 DomainsDatabaseHelper.ENABLED -> {
1220 // Set the icon color.
1221 wideViewportImageView.isSelected = true
1223 // Hide the wide viewport text view.
1224 wideViewportTextView.visibility = View.GONE
1227 DomainsDatabaseHelper.DISABLED -> {
1228 // Set the icon color.
1229 wideViewportImageView.isSelected = false
1231 // Hid ethe wide viewport text view.
1232 wideViewportTextView.visibility = View.GONE
1237 override fun onNothingSelected(parent: AdapterView<*>?) {
1242 // Set the display webpage images spinner listener.
1243 displayWebpageImagesSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1244 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1245 // Update the icon and the visibility of the display images text view.
1247 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1248 // Set the icon color.
1249 displayWebpageImagesImageView.isSelected = defaultDisplayWebpageImages
1251 // Show the display images text view.
1252 displayImagesTextView.visibility = View.VISIBLE
1255 DomainsDatabaseHelper.ENABLED -> {
1256 // Set the icon color.
1257 displayWebpageImagesImageView.isSelected = true
1259 // Hide the display images text view.
1260 displayImagesTextView.visibility = View.GONE
1263 DomainsDatabaseHelper.DISABLED -> {
1264 // Set the icon color.
1265 displayWebpageImagesImageView.isSelected = false
1267 // Hide the display images text view.
1268 displayImagesTextView.visibility = View.GONE
1273 override fun onNothingSelected(parent: AdapterView<*>?) {
1278 // Set the pinned SSL certificate switch listener.
1279 pinnedSslCertificateSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1280 // Update the icon color.
1281 pinnedSslCertificateImageView.isSelected = isChecked
1283 // Update the views.
1284 if (isChecked) { // SSL certificate pinning is enabled.
1285 // Update the visibility of the saved SSL certificate.
1286 if (savedSslIssuedToCNameString == null)
1287 savedSslCardView.visibility = View.GONE
1289 savedSslCardView.visibility = View.VISIBLE
1291 // Update the visibility of the current website SSL certificate.
1292 if (DomainsActivity.sslIssuedToCName == null) {
1293 // Hide the SSL certificate.
1294 currentSslCardView.visibility = View.GONE
1296 // Show the instruction.
1297 noCurrentWebsiteCertificateTextView.visibility = View.VISIBLE
1299 // Show the SSL certificate.
1300 currentSslCardView.visibility = View.VISIBLE
1302 // Hide the instruction.
1303 noCurrentWebsiteCertificateTextView.visibility = View.GONE
1306 // Set the status of the radio buttons.
1307 if (savedSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is displayed.
1308 // Check the saved SSL certificate radio button.
1309 savedSslCertificateRadioButton.isChecked = true
1311 // Uncheck the current website SSL certificate radio button.
1312 currentWebsiteCertificateRadioButton.isChecked = false
1314 // Set the background of the saved SSL certificate linear layout to be transparent.
1315 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1317 // Darken the background of the current website SSL certificate linear layout according to the theme.
1318 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1320 // Scroll to the current website SSL certificate card.
1321 savedSslCardView.parent.requestChildFocus(savedSslCardView, savedSslCardView)
1322 } else if (currentSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1323 // Check the current website SSL certificate radio button.
1324 currentWebsiteCertificateRadioButton.isChecked = true
1326 // Uncheck the saved SSL certificate radio button.
1327 savedSslCertificateRadioButton.isChecked = false
1329 // Set the background of the current website SSL certificate linear layout to be transparent.
1330 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1332 // Darken the background of the saved SSL certificate linear layout according to the theme.
1333 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1335 // Scroll to the current website SSL certificate card.
1336 currentSslCardView.parent.requestChildFocus(currentSslCardView, currentSslCardView)
1337 } else { // Neither SSL certificate is visible.
1338 // Uncheck both radio buttons.
1339 savedSslCertificateRadioButton.isChecked = false
1340 currentWebsiteCertificateRadioButton.isChecked = false
1342 // Scroll to the current website SSL certificate card.
1343 noCurrentWebsiteCertificateTextView.parent.requestChildFocus(noCurrentWebsiteCertificateTextView, noCurrentWebsiteCertificateTextView)
1345 } else { // SSL certificate pinning is disabled.
1346 // Hide the SSl certificates and instructions.
1347 savedSslCardView.visibility = View.GONE
1348 currentSslCardView.visibility = View.GONE
1349 noCurrentWebsiteCertificateTextView.visibility = View.GONE
1351 // Uncheck the radio buttons.
1352 savedSslCertificateRadioButton.isChecked = false
1353 currentWebsiteCertificateRadioButton.isChecked = false
1357 // Set the saved SSL card view listener.
1358 savedSslCardView.setOnClickListener {
1359 // Check the saved SSL certificate radio button.
1360 savedSslCertificateRadioButton.isChecked = true
1362 // Uncheck the current website SSL certificate radio button.
1363 currentWebsiteCertificateRadioButton.isChecked = false
1365 // Set the background of the saved SSL certificate linear layout to be transparent.
1366 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1368 // Darken the background of the current website SSL certificate linear layout.
1369 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1372 // Set the saved SSL certificate radio button listener.
1373 savedSslCertificateRadioButton.setOnClickListener {
1374 // Check the saved SSL certificate radio button.
1375 savedSslCertificateRadioButton.isChecked = true
1377 // Uncheck the current website SSL certificate radio button.
1378 currentWebsiteCertificateRadioButton.isChecked = false
1380 // Set the background of the saved SSL certificate linear layout to be transparent.
1381 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1383 // Darken the background of the current website SSL certificate linear layout.
1384 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1387 // Set the current SSL card view listener.
1388 currentSslCardView.setOnClickListener {
1389 // Check the current website SSL certificate radio button.
1390 currentWebsiteCertificateRadioButton.isChecked = true
1392 // Uncheck the saved SSL certificate radio button.
1393 savedSslCertificateRadioButton.isChecked = false
1395 // Set the background of the current website SSL certificate linear layout to be transparent.
1396 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1398 // Darken the background of the saved SSL certificate linear layout.
1399 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1402 // Set the current website certificate radio button listener.
1403 currentWebsiteCertificateRadioButton.setOnClickListener {
1404 // Check the current website SSL certificate radio button.
1405 currentWebsiteCertificateRadioButton.isChecked = true
1407 // Uncheck the saved SSL certificate radio button.
1408 savedSslCertificateRadioButton.isChecked = false
1410 // Set the background of the current website SSL certificate linear layout to be transparent.
1411 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1413 // Darken the background of the saved SSL certificate linear layout.
1414 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1417 // Set the pinned IP addresses switch listener.
1418 pinnedIpAddressesSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1419 // Update the icon color.
1420 pinnedIpAddressesImageView.isSelected = isChecked
1422 // Update the views.
1423 if (isChecked) { // IP addresses pinning is enabled.
1424 // Update the visibility of the saved IP addresses card view.
1425 if (savedIpAddresses == null)
1426 savedIpAddressesCardView.visibility = View.GONE
1428 savedIpAddressesCardView.visibility = View.VISIBLE
1430 // Show the current IP addresses card view.
1431 currentIpAddressesCardView.visibility = View.VISIBLE
1433 // Set the status of the radio buttons.
1434 if (savedIpAddressesCardView.visibility == View.VISIBLE) { // The saved IP addresses are visible.
1435 // Check the saved IP addresses radio button.
1436 savedIpAddressesRadioButton.isChecked = true
1438 // Uncheck the current IP addresses radio button.
1439 currentIpAddressesRadioButton.isChecked = false
1441 // Set the background of the saved IP addresses linear layout to be transparent.
1442 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1444 // Darken the background of the current IP addresses linear layout.
1445 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1446 } else { // The saved IP addresses are not visible.
1447 // Check the current IP addresses radio button.
1448 currentIpAddressesRadioButton.isChecked = true
1450 // Uncheck the saved IP addresses radio button.
1451 savedIpAddressesRadioButton.isChecked = false
1453 // Set the background of the current IP addresses linear layout to be transparent.
1454 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1456 // Darken the background of the saved IP addresses linear layout.
1457 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1460 // Scroll to the bottom of the card views.
1461 currentIpAddressesCardView.parent.requestChildFocus(currentIpAddressesCardView, currentIpAddressesCardView)
1462 } else { // IP addresses pinning is disabled.
1463 // Hide the IP addresses card views.
1464 savedIpAddressesCardView.visibility = View.GONE
1465 currentIpAddressesCardView.visibility = View.GONE
1467 // Uncheck the radio buttons.
1468 savedIpAddressesRadioButton.isChecked = false
1469 currentIpAddressesRadioButton.isChecked = false
1473 // Set the saved IP addresses card view listener.
1474 savedIpAddressesCardView.setOnClickListener {
1475 // Check the saved IP addresses radio button.
1476 savedIpAddressesRadioButton.isChecked = true
1478 // Uncheck the current website IP addresses radio button.
1479 currentIpAddressesRadioButton.isChecked = false
1481 // Set the background of the saved IP addresses linear layout to be transparent.
1482 savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1484 // Darken the background of the current IP addresses linear layout.
1485 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1488 // Set the saved IP addresses radio button listener.
1489 savedIpAddressesRadioButton.setOnClickListener {
1490 // Check the saved IP addresses radio button.
1491 savedIpAddressesRadioButton.isChecked = true
1493 // Uncheck the current website IP addresses radio button.
1494 currentIpAddressesRadioButton.isChecked = false
1496 // Set the background of the saved IP addresses linear layout to be transparent.
1497 savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1499 // Darken the background of the current IP addresses linear layout.
1500 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1503 // Set the current IP addresses card view listener.
1504 currentIpAddressesCardView.setOnClickListener {
1505 // Check the current IP addresses radio button.
1506 currentIpAddressesRadioButton.isChecked = true
1508 // Uncheck the saved IP addresses radio button.
1509 savedIpAddressesRadioButton.isChecked = false
1511 // Set the background of the current IP addresses linear layout to be transparent.
1512 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1514 // Darken the background of the saved IP addresses linear layout.
1515 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1518 // Set the current IP addresses radio button listener.
1519 currentIpAddressesRadioButton.setOnClickListener {
1520 // Check the current IP addresses radio button.
1521 currentIpAddressesRadioButton.isChecked = true
1523 // Uncheck the saved IP addresses radio button.
1524 savedIpAddressesRadioButton.isChecked = false
1526 // Set the background of the current IP addresses linear layout to be transparent.
1527 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1529 // Darken the background of the saved IP addresses linear layout.
1530 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1533 // Set the scroll Y.
1534 domainSettingsScrollView.post { domainSettingsScrollView.scrollY = scrollY }
1536 // Return the domain settings view.
1537 return domainSettingsView
1540 private fun checkDomainNameAgainstCertificate(domainName: String?, certificateCommonName: String?): Boolean {
1541 // Initialize the domain names match tracker.
1542 var domainNamesMatch = false
1544 // Check various wildcard permutations if the domain name and the certificate Common Name are not empty.
1545 if ((domainName != null) && (certificateCommonName != null)) {
1546 // Check if the domains match.
1547 if (domainName == certificateCommonName)
1548 domainNamesMatch = true
1550 // If the domain name starts with a wildcard, check the base domain against all the subdomains of the certificate Common Name.
1551 if (!domainNamesMatch && domainName.startsWith("*.") && domainName.length > 2) {
1552 // Remove the initial `*.`.
1553 val baseDomainName = domainName.substring(2)
1555 // Create a copy of the certificate Common Name to test subdomains.
1556 var certificateCommonNameSubdomain: String = certificateCommonName
1558 // Check all the subdomains in the certificate Common Name subdomain against the base domain name.
1559 while (!domainNamesMatch && certificateCommonNameSubdomain.contains(".")) { // Stop checking if the domain names match or if there are no more dots.
1560 // Test the certificate Common Name subdomain against the base domain name.
1561 if (certificateCommonNameSubdomain == baseDomainName)
1562 domainNamesMatch = true
1564 // Strip out the lowest subdomain of the certificate Common Name subdomain.
1565 certificateCommonNameSubdomain = try {
1566 certificateCommonNameSubdomain.substring(certificateCommonNameSubdomain.indexOf(".") + 1)
1567 } catch (e: IndexOutOfBoundsException) { // The certificate Common Name subdomain ends with a dot.
1573 // If the certificate Common Name starts with a wildcard, check the base common name against all the subdomains of the domain name.
1574 if (!domainNamesMatch && certificateCommonName.startsWith("*.") && certificateCommonName.length > 2) {
1575 // Remove the initial `*.`.
1576 val baseCertificateCommonName = certificateCommonName.substring(2)
1578 // Setup a copy of domain name to test subdomains.
1579 var domainNameSubdomain: String = domainName
1581 // Check all the subdomains in the domain name subdomain against the base certificate Common Name.
1582 while (!domainNamesMatch && domainNameSubdomain.contains(".") && domainNameSubdomain.length > 2) {
1583 // Test the domain name subdomain against the base certificate Common Name.
1584 if (domainNameSubdomain == baseCertificateCommonName)
1585 domainNamesMatch = true
1587 // Strip out the lowest subdomain of the domain name subdomain.
1588 domainNameSubdomain = try {
1589 domainNameSubdomain.substring(domainNameSubdomain.indexOf(".") + 1)
1590 } catch (e: IndexOutOfBoundsException) { // `domainNameSubdomain` ends with a dot.
1596 // If both names start with a wildcard, check if the root of one contains the root of the other.
1597 if (!domainNamesMatch && domainName.startsWith("*.") && domainName.length > 2 && certificateCommonName.startsWith("*.") && certificateCommonName.length > 2) {
1598 // Remove the wildcards.
1599 val rootDomainName = domainName.substring(2)
1600 val rootCertificateCommonName = certificateCommonName.substring(2)
1602 // Check if one name ends with the contents of the other. If so, there will be overlap in the their wildcard subdomains.
1603 if (rootDomainName.endsWith(rootCertificateCommonName) || rootCertificateCommonName.endsWith(rootDomainName))
1604 domainNamesMatch = true
1608 return domainNamesMatch