2 * Copyright 2017-2023 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
6 * Privacy Browser Android is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * Privacy Browser Android is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Browser Android. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacybrowser.fragments
22 import android.annotation.SuppressLint
23 import android.content.res.Configuration
24 import android.os.Build
25 import android.os.Bundle
26 import android.text.Editable
27 import android.text.SpannableStringBuilder
28 import android.text.Spanned
29 import android.text.TextWatcher
30 import android.text.style.ForegroundColorSpan
31 import android.view.LayoutInflater
32 import android.view.View
33 import android.view.ViewGroup
34 import android.webkit.WebView
35 import android.widget.AdapterView
36 import android.widget.ArrayAdapter
37 import android.widget.CompoundButton
38 import android.widget.EditText
39 import android.widget.ImageView
40 import android.widget.LinearLayout
41 import android.widget.RadioButton
42 import android.widget.ScrollView
43 import android.widget.Spinner
44 import android.widget.TextView
46 import androidx.appcompat.widget.SwitchCompat
47 import androidx.cardview.widget.CardView
48 import androidx.core.content.ContextCompat.getColor
49 import androidx.core.content.res.ResourcesCompat
50 import androidx.fragment.app.Fragment
51 import androidx.preference.PreferenceManager
53 import com.stoutner.privacybrowser.R
54 import com.stoutner.privacybrowser.activities.DOMAINS_CUSTOM_USER_AGENT
55 import com.stoutner.privacybrowser.activities.DOMAINS_SYSTEM_DEFAULT_USER_AGENT
56 import com.stoutner.privacybrowser.activities.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT
57 import com.stoutner.privacybrowser.activities.SETTINGS_CUSTOM_USER_AGENT
58 import com.stoutner.privacybrowser.activities.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT
59 import com.stoutner.privacybrowser.activities.UNRECOGNIZED_USER_AGENT
60 import com.stoutner.privacybrowser.activities.DomainsActivity
61 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper
63 import java.lang.IndexOutOfBoundsException
64 import java.text.DateFormat
65 import java.util.Calendar
68 class DomainSettingsFragment : Fragment() {
69 // Define the class variables.
70 private var scrollY = 0
73 // Define the public constants.
74 const val DATABASE_ID = "database_id"
75 const val SCROLL_Y = "scroll_y"
77 // Define the public variables. `databaseId` is public so it can be accessed from `DomainsActivity`.
81 override fun onCreate(savedInstanceState: Bundle?) {
82 // Run the default commands.
83 super.onCreate(savedInstanceState)
85 // Store the arguments in class variables.
86 databaseId = requireArguments().getInt(DATABASE_ID)
87 scrollY = requireArguments().getInt(SCROLL_Y)
90 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
91 // Inflate the layout. The fragment will take care of attaching the root automatically.
92 val domainSettingsView = inflater.inflate(R.layout.domain_settings_fragment, container, false)
94 // Get the current theme status.
95 val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
97 // Get a handle for the shared preference.
98 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
100 // Store the default settings.
101 val defaultUserAgentName = sharedPreferences.getString(getString(R.string.user_agent_key), getString(R.string.user_agent_default_value))
102 val defaultCustomUserAgentString = sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value))
103 val defaultFontSizeString = sharedPreferences.getString(getString(R.string.font_size_key), getString(R.string.font_size_default_value))
104 val defaultSwipeToRefresh = sharedPreferences.getBoolean(getString(R.string.swipe_to_refresh_key), true)
105 val defaultWebViewTheme = sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value))
106 val defaultWideViewport = sharedPreferences.getBoolean(getString(R.string.wide_viewport_key), true)
107 val defaultDisplayWebpageImages = sharedPreferences.getBoolean(getString(R.string.display_webpage_images_key), true)
109 // Get handles for the views.
110 val domainSettingsScrollView = domainSettingsView.findViewById<ScrollView>(R.id.domain_settings_scrollview)
111 val domainNameEditText = domainSettingsView.findViewById<EditText>(R.id.domain_settings_name_edittext)
112 val javaScriptImageView = domainSettingsView.findViewById<ImageView>(R.id.javascript_imageview)
113 val javaScriptSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.javascript_switch)
114 val cookiesImageView = domainSettingsView.findViewById<ImageView>(R.id.cookies_imageview)
115 val cookiesSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.cookies_switch)
116 val domStorageImageView = domainSettingsView.findViewById<ImageView>(R.id.dom_storage_imageview)
117 val domStorageSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.dom_storage_switch)
118 val formDataImageView = domainSettingsView.findViewById<ImageView>(R.id.form_data_imageview) // The form data views can be remove once the minimum API >= 26.
119 val formDataSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.form_data_switch) // The form data views can be remove once the minimum API >= 26.
120 val easyListImageView = domainSettingsView.findViewById<ImageView>(R.id.easylist_imageview)
121 val easyListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.easylist_switch)
122 val easyPrivacyImageView = domainSettingsView.findViewById<ImageView>(R.id.easyprivacy_imageview)
123 val easyPrivacySwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.easyprivacy_switch)
124 val fanboysAnnoyanceListImageView = domainSettingsView.findViewById<ImageView>(R.id.fanboys_annoyance_list_imageview)
125 val fanboysAnnoyanceListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.fanboys_annoyance_list_switch)
126 val fanboysSocialBlockingListImageView = domainSettingsView.findViewById<ImageView>(R.id.fanboys_social_blocking_list_imageview)
127 val fanboysSocialBlockingListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.fanboys_social_blocking_list_switch)
128 val ultraListImageView = domainSettingsView.findViewById<ImageView>(R.id.ultralist_imageview)
129 val ultraListSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.ultralist_switch)
130 val ultraPrivacyImageView = domainSettingsView.findViewById<ImageView>(R.id.ultraprivacy_imageview)
131 val ultraPrivacySwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.ultraprivacy_switch)
132 val blockAllThirdPartyRequestsImageView = domainSettingsView.findViewById<ImageView>(R.id.block_all_third_party_requests_imageview)
133 val blockAllThirdPartyRequestsSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.block_all_third_party_requests_switch)
134 val userAgentLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.user_agent_linearlayout)
135 val userAgentSpinner = domainSettingsView.findViewById<Spinner>(R.id.user_agent_spinner)
136 val userAgentTextView = domainSettingsView.findViewById<TextView>(R.id.user_agent_textview)
137 val customUserAgentEditText = domainSettingsView.findViewById<EditText>(R.id.custom_user_agent_edittext)
138 val fontSizeLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.font_size_linearlayout)
139 val fontSizeSpinner = domainSettingsView.findViewById<Spinner>(R.id.font_size_spinner)
140 val defaultFontSizeTextView = domainSettingsView.findViewById<TextView>(R.id.default_font_size_textview)
141 val customFontSizeEditText = domainSettingsView.findViewById<EditText>(R.id.custom_font_size_edittext)
142 val swipeToRefreshLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.swipe_to_refresh_linearlayout)
143 val swipeToRefreshImageView = domainSettingsView.findViewById<ImageView>(R.id.swipe_to_refresh_imageview)
144 val swipeToRefreshSpinner = domainSettingsView.findViewById<Spinner>(R.id.swipe_to_refresh_spinner)
145 val swipeToRefreshTextView = domainSettingsView.findViewById<TextView>(R.id.swipe_to_refresh_textview)
146 val webViewThemeLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.webview_theme_linearlayout)
147 val webViewThemeImageView = domainSettingsView.findViewById<ImageView>(R.id.webview_theme_imageview)
148 val webViewThemeSpinner = domainSettingsView.findViewById<Spinner>(R.id.webview_theme_spinner)
149 val webViewThemeTextView = domainSettingsView.findViewById<TextView>(R.id.webview_theme_textview)
150 val wideViewportLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.wide_viewport_linearlayout)
151 val wideViewportImageView = domainSettingsView.findViewById<ImageView>(R.id.wide_viewport_imageview)
152 val wideViewportSpinner = domainSettingsView.findViewById<Spinner>(R.id.wide_viewport_spinner)
153 val wideViewportTextView = domainSettingsView.findViewById<TextView>(R.id.wide_viewport_textview)
154 val displayImagesLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.display_images_linearlayout)
155 val displayImagesImageView = domainSettingsView.findViewById<ImageView>(R.id.display_images_imageview)
156 val displayImagesSpinner = domainSettingsView.findViewById<Spinner>(R.id.display_images_spinner)
157 val displayImagesTextView = domainSettingsView.findViewById<TextView>(R.id.display_images_textview)
158 val pinnedSslCertificateImageView = domainSettingsView.findViewById<ImageView>(R.id.pinned_ssl_certificate_imageview)
159 val pinnedSslCertificateSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.pinned_ssl_certificate_switch)
160 val savedSslCardView = domainSettingsView.findViewById<CardView>(R.id.saved_ssl_certificate_cardview)
161 val savedSslCertificateLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.saved_ssl_certificate_linearlayout)
162 val savedSslCertificateRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.saved_ssl_certificate_radiobutton)
163 val savedSslIssuedToCNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_cname)
164 val savedSslIssuedToONameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_oname)
165 val savedSslIssuedToUNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_to_uname)
166 val savedSslIssuedByCNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_cname)
167 val savedSslIssuedByONameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_oname)
168 val savedSslIssuedByUNameTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_issued_by_uname)
169 val savedSslStartDateTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_start_date)
170 val savedSslEndDateTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ssl_certificate_end_date)
171 val currentSslCardView = domainSettingsView.findViewById<CardView>(R.id.current_website_certificate_cardview)
172 val currentWebsiteCertificateLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.current_website_certificate_linearlayout)
173 val currentWebsiteCertificateRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.current_website_certificate_radiobutton)
174 val currentSslIssuedToCNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_cname)
175 val currentSslIssuedToONameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_oname)
176 val currentSslIssuedToUNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_to_uname)
177 val currentSslIssuedByCNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_cname)
178 val currentSslIssuedByONameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_oname)
179 val currentSslIssuedByUNameTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_issued_by_uname)
180 val currentSslStartDateTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_start_date)
181 val currentSslEndDateTextView = domainSettingsView.findViewById<TextView>(R.id.current_website_certificate_end_date)
182 val noCurrentWebsiteCertificateTextView = domainSettingsView.findViewById<TextView>(R.id.no_current_website_certificate)
183 val pinnedIpAddressesImageView = domainSettingsView.findViewById<ImageView>(R.id.pinned_ip_addresses_imageview)
184 val pinnedIpAddressesSwitch = domainSettingsView.findViewById<SwitchCompat>(R.id.pinned_ip_addresses_switch)
185 val savedIpAddressesCardView = domainSettingsView.findViewById<CardView>(R.id.saved_ip_addresses_cardview)
186 val savedIpAddressesLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.saved_ip_addresses_linearlayout)
187 val savedIpAddressesRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.saved_ip_addresses_radiobutton)
188 val savedIpAddressesTextView = domainSettingsView.findViewById<TextView>(R.id.saved_ip_addresses_textview)
189 val currentIpAddressesCardView = domainSettingsView.findViewById<CardView>(R.id.current_ip_addresses_cardview)
190 val currentIpAddressesLinearLayout = domainSettingsView.findViewById<LinearLayout>(R.id.current_ip_addresses_linearlayout)
191 val currentIpAddressesRadioButton = domainSettingsView.findViewById<RadioButton>(R.id.current_ip_addresses_radiobutton)
192 val currentIpAddressesTextView = domainSettingsView.findViewById<TextView>(R.id.current_ip_addresses_textview)
194 // Hide the WebView theme linear layout if the API < 29.
195 if (Build.VERSION.SDK_INT < 29)
196 webViewThemeLinearLayout.visibility = View.GONE
198 // Setup the pinned labels.
199 val cNameLabel = getString(R.string.common_name)
200 val oNameLabel = getString(R.string.organization)
201 val uNameLabel = getString(R.string.organizational_unit)
202 val startDateLabel = getString(R.string.start_date)
203 val endDateLabel = getString(R.string.end_date)
205 // Initialize the database handler.
206 val domainsDatabaseHelper = DomainsDatabaseHelper(requireContext())
208 // Get the database cursor for this ID.
209 val domainCursor = domainsDatabaseHelper.getCursorForId(databaseId)
211 // Move to the first row.
212 domainCursor.moveToFirst()
214 // Save the cursor entries as variables.
215 val domainNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME))
216 val javaScriptInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT))
217 val cookiesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES))
218 val domStorageInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE))
219 val formDataInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA)) // Form data can be remove once the minimum API >= 26.
220 val easyListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST))
221 val easyPrivacyInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY))
222 val fanboysAnnoyanceListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST))
223 val fanboysSocialBlockingListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST))
224 val ultraListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST))
225 val ultraPrivacyInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY))
226 val blockAllThirdPartyRequestsInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS))
227 val currentUserAgentName = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT))
228 val fontSizeInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE))
229 val swipeToRefreshInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH))
230 val webViewThemeInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME))
231 val wideViewportInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WIDE_VIEWPORT))
232 val displayImagesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DISPLAY_IMAGES))
233 val pinnedSslCertificateInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE))
234 val savedSslIssuedToCNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME))
235 val savedSslIssuedToONameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION))
236 val savedSslIssuedToUNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT))
237 val savedSslIssuedByCNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME))
238 val savedSslIssuedByONameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION))
239 val savedSslIssuedByUNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT))
240 val pinnedIpAddressesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_IP_ADDRESSES))
241 val savedIpAddresses = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.IP_ADDRESSES))
243 // Get the SSL dates from the database.
244 val savedSslStartDateLong = domainCursor.getLong(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_START_DATE))
245 val savedSslEndDateLong = domainCursor.getLong(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_END_DATE))
247 // Initialize the saved SSL certificate date variables.
248 var savedSslStartDate: Date? = null
249 var savedSslEndDate: Date? = null
251 // Only get the saved SSL certificate dates from the cursor if they are not set to `0`.
252 if (savedSslStartDateLong != 0L)
253 savedSslStartDate = Date(savedSslStartDateLong)
254 if (savedSslEndDateLong != 0L)
255 savedSslEndDate = Date(savedSslEndDateLong)
257 // Create array adapters for the spinners.
258 val translatedUserAgentArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.translated_domain_settings_user_agent_names, R.layout.spinner_item)
259 val fontSizeArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.font_size_array, R.layout.spinner_item)
260 val swipeToRefreshArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.swipe_to_refresh_array, R.layout.spinner_item)
261 val webViewThemeArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.webview_theme_array, R.layout.spinner_item)
262 val wideViewportArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.wide_viewport_array, R.layout.spinner_item)
263 val displayImagesArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.display_webpage_images_array, R.layout.spinner_item)
265 // Set the drop down view resource on the spinners.
266 translatedUserAgentArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
267 fontSizeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
268 swipeToRefreshArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
269 webViewThemeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
270 wideViewportArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
271 displayImagesArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items)
273 // Set the array adapters for the spinners.
274 userAgentSpinner.adapter = translatedUserAgentArrayAdapter
275 fontSizeSpinner.adapter = fontSizeArrayAdapter
276 swipeToRefreshSpinner.adapter = swipeToRefreshArrayAdapter
277 webViewThemeSpinner.adapter = webViewThemeArrayAdapter
278 wideViewportSpinner.adapter = wideViewportArrayAdapter
279 displayImagesSpinner.adapter = displayImagesArrayAdapter
281 // Create a spannable string builder for each TextView that needs multiple colors of text.
282 val savedSslIssuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString)
283 val savedSslIssuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + savedSslIssuedToONameString)
284 val savedSslIssuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + savedSslIssuedToUNameString)
285 val savedSslIssuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedByCNameString)
286 val savedSslIssuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + savedSslIssuedByONameString)
287 val savedSslIssuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + savedSslIssuedByUNameString)
289 // Create the date spannable string builders.
290 val savedSslStartDateStringBuilder: SpannableStringBuilder = if (savedSslStartDate == null)
291 SpannableStringBuilder(startDateLabel)
293 SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslStartDate))
295 val savedSslEndDateStringBuilder: SpannableStringBuilder = if (savedSslEndDate == null)
296 SpannableStringBuilder(endDateLabel)
298 SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslEndDate))
300 // Create the color spans.
301 val blueColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.alt_blue_text))
302 val redColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.red_text))
304 // Set the domain name from the the database cursor.
305 domainNameEditText.setText(domainNameString)
307 // Update the certificates' Common Name color when the domain name text changes.
308 domainNameEditText.addTextChangedListener(object : TextWatcher {
309 override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
313 override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
317 override fun afterTextChanged(s: Editable) {
318 // Get the new domain name.
319 val newDomainName = domainNameEditText.text.toString()
321 // Check the saved SSL certificate against the new domain name.
322 val savedSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, savedSslIssuedToCNameString)
324 // Create a spannable string builder for the saved certificate's Common Name.
325 val savedSslCNameStringBuilder = SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString)
327 // Format the saved certificate's Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
328 if (savedSslMatchesNewDomainName) {
329 savedSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
331 savedSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, savedSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
334 // Update the saved SSL issued to CName text view.
335 savedSslIssuedToCNameTextView.text = savedSslCNameStringBuilder
337 // Update the current website certificate if it exists.
338 if (DomainsActivity.sslIssuedToCName != null) {
339 // Check the current website certificate against the new domain name.
340 val currentSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, DomainsActivity.sslIssuedToCName)
342 // Create a spannable string builder for the current website certificate's Common Name.
343 val currentSslCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName)
345 // Format the current certificate Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
346 if (currentSslMatchesNewDomainName) {
347 currentSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
349 currentSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, currentSslCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
352 // Update the current SSL issued to CName text view.
353 currentSslIssuedToCNameTextView.text = currentSslCNameStringBuilder
358 // Set the switch positions.
359 javaScriptSwitch.isChecked = (javaScriptInt == 1)
360 cookiesSwitch.isChecked = (cookiesInt == 1)
361 domStorageSwitch.isChecked = (domStorageInt == 1)
362 formDataSwitch.isChecked = (formDataInt == 1) // Form data can be removed once the minimum API >= 26.
363 easyListSwitch.isChecked = (easyListInt == 1)
364 easyPrivacySwitch.isChecked = (easyPrivacyInt == 1)
365 fanboysAnnoyanceListSwitch.isChecked = (fanboysAnnoyanceListInt == 1)
366 fanboysSocialBlockingListSwitch.isChecked = (fanboysSocialBlockingListInt == 1)
367 ultraListSwitch.isChecked = (ultraListInt == 1)
368 ultraPrivacySwitch.isChecked = (ultraPrivacyInt == 1)
369 blockAllThirdPartyRequestsSwitch.isChecked = (blockAllThirdPartyRequestsInt == 1)
370 pinnedSslCertificateSwitch.isChecked = (pinnedSslCertificateInt == 1)
371 pinnedIpAddressesSwitch.isChecked = (pinnedIpAddressesInt == 1)
373 // Set the switch icon colors.
374 cookiesImageView.isSelected = (cookiesInt == 1)
375 domStorageImageView.isSelected = (domStorageInt == 1)
376 formDataImageView.isSelected = (formDataInt == 1) // Form data can be removed once the minimum API >= 26.
377 easyListImageView.isSelected = (easyListInt == 1)
378 easyPrivacyImageView.isSelected = (easyPrivacyInt == 1)
379 fanboysAnnoyanceListImageView.isSelected = (fanboysAnnoyanceListInt == 1)
380 fanboysSocialBlockingListImageView.isSelected = (fanboysSocialBlockingListInt == 1)
381 ultraListImageView.isSelected = (ultraListInt == 1)
382 ultraPrivacyImageView.isSelected = (ultraPrivacyInt == 1)
383 blockAllThirdPartyRequestsImageView.isSelected = (blockAllThirdPartyRequestsInt == 1)
384 pinnedSslCertificateImageView.isSelected = (pinnedSslCertificateInt == 1)
385 pinnedIpAddressesImageView.isSelected = (pinnedIpAddressesInt == 1)
387 // Set the JavaScript icon.
388 if (javaScriptInt == 1)
389 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null))
391 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null))
393 // Set the DOM storage switch status based on the JavaScript status.
394 domStorageSwitch.isEnabled = (javaScriptInt == 1)
396 // Set the DOM storage icon ghosted status based on the JavaScript status.
397 domStorageImageView.isEnabled = (javaScriptInt == 1)
399 // Set the form data visibility. Form data can be removed once the minimum API >= 26.
400 if (Build.VERSION.SDK_INT >= 26) {
401 // Hide the form data image view and switch.
402 formDataImageView.visibility = View.GONE
403 formDataSwitch.visibility = View.GONE
406 // Set Fanboy's Social Blocking List switch status based on the Annoyance List status.
407 fanboysSocialBlockingListSwitch.isEnabled = (fanboysAnnoyanceListInt == 0)
409 // Set the Social Blocking List icon ghosted status based on the Annoyance List status.
410 fanboysSocialBlockingListImageView.isEnabled = (fanboysAnnoyanceListInt == 0)
412 // Open the spinners when the text view is clicked.
413 userAgentTextView.setOnClickListener { userAgentSpinner.performClick() }
414 defaultFontSizeTextView.setOnClickListener { fontSizeSpinner.performClick() }
415 swipeToRefreshTextView.setOnClickListener { swipeToRefreshSpinner.performClick() }
416 webViewThemeTextView.setOnClickListener { webViewThemeSpinner.performClick() }
417 wideViewportTextView.setOnClickListener { wideViewportSpinner.performClick() }
418 displayImagesTextView.setOnClickListener { displayImagesSpinner.performClick() }
420 // Inflated a WebView to get the default user agent.
421 // `@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.
422 @SuppressLint("InflateParams") val bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false)
423 val bareWebView = bareWebViewLayout.findViewById<WebView>(R.id.bare_webview)
424 val webViewDefaultUserAgentString = bareWebView.settings.userAgentString
426 // Get a handle for the user agent array adapter. This array does not contain the `System default` entry.
427 val userAgentNamesArray = ArrayAdapter.createFromResource(requireContext(), R.array.user_agent_names, R.layout.spinner_item)
429 // Get the positions of the user agent and the default user agent.
430 val userAgentArrayPosition = userAgentNamesArray.getPosition(currentUserAgentName)
431 val defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName)
433 // Get a handle for the user agent data array. This array does not contain the `System default` entry.
434 val userAgentDataArray = resources.getStringArray(R.array.user_agent_data)
436 // Set the user agent text.
437 if (currentUserAgentName == getString(R.string.system_default_user_agent)) { // Use the system default user agent.
438 // Set the user agent according to the system default.
439 when (defaultUserAgentArrayPosition) {
440 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
441 UNRECOGNIZED_USER_AGENT -> userAgentTextView.text = defaultUserAgentName
443 // Display the WebView default user agent.
444 SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentTextView.text = webViewDefaultUserAgentString
446 // Display the custom user agent.
447 SETTINGS_CUSTOM_USER_AGENT -> userAgentTextView.text = defaultCustomUserAgentString
449 // Get the user agent string from the user agent data array.
450 else -> userAgentTextView.text = userAgentDataArray[defaultUserAgentArrayPosition]
453 // Set the background color to be transparent.
454 userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
455 } else if (userAgentArrayPosition == UNRECOGNIZED_USER_AGENT || currentUserAgentName == getString(R.string.custom_user_agent)) {
456 // 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.
457 // Set the user agent spinner to `Custom user agent`.
458 userAgentSpinner.setSelection(DOMAINS_CUSTOM_USER_AGENT)
460 // Hide the user agent text view.
461 userAgentTextView.visibility = View.GONE
463 // Show the custom user agent edit text and set the current user agent name as the text.
464 customUserAgentEditText.visibility = View.VISIBLE
465 customUserAgentEditText.setText(currentUserAgentName)
467 // Set the background color to be blue.
468 userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
469 } else { // The user agent name contains one of the canonical user agents.
470 // 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.
471 userAgentSpinner.setSelection(userAgentArrayPosition + 1)
473 // Show the user agent text view.
474 userAgentTextView.visibility = View.VISIBLE
476 // Hide the custom user agent edit text.
477 customUserAgentEditText.visibility = View.GONE
479 // Set the user agent text.
480 if (userAgentArrayPosition == DOMAINS_WEBVIEW_DEFAULT_USER_AGENT) { // The WebView default user agent is selected.
481 // Display the WebView default user agent.
482 userAgentTextView.text = webViewDefaultUserAgentString
483 } else { // A user agent besides the default is selected.
484 // 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.
485 userAgentTextView.text = userAgentDataArray[userAgentArrayPosition + 1]
488 // Set the background color to be blue.
489 userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
493 // Display the font size settings.
494 if (fontSizeInt == 0) { // `0` is the code for system default font size.
495 // Set the font size to the system default.
496 fontSizeSpinner.setSelection(0)
498 // Show the default font size text view.
499 defaultFontSizeTextView.visibility = View.VISIBLE
501 // Hide the custom font size edit text.
502 customFontSizeEditText.visibility = View.GONE
504 // 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.
505 customFontSizeEditText.setText(defaultFontSizeString)
507 // Set the background color to be transparent.
508 fontSizeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
509 } else { // A custom font size is selected.
510 // Set the spinner to the custom font size.
511 fontSizeSpinner.setSelection(1)
513 // Hide the default font size text view.
514 defaultFontSizeTextView.visibility = View.GONE
516 // Show the custom font size edit text.
517 customFontSizeEditText.visibility = View.GONE
519 // Set the custom font size.
520 customFontSizeEditText.setText(fontSizeInt.toString())
522 // Set the background color to be blue.
523 fontSizeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
526 // Initialize the default font size percentage string.
527 val defaultFontSizePercentageString = "$defaultFontSizeString%"
529 // Set the default font size text in the text view.
530 defaultFontSizeTextView.text = defaultFontSizePercentageString
532 // Select the swipe-to-refresh selection in the spinner.
533 swipeToRefreshSpinner.setSelection(swipeToRefreshInt)
535 // Set the swipe-to-refresh text.
536 if (defaultSwipeToRefresh)
537 swipeToRefreshTextView.text = swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
539 swipeToRefreshTextView.text = swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
541 // Set the swipe-to-refresh icon and text view settings.
542 when (swipeToRefreshInt) {
543 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
544 // Set the icon color.
545 swipeToRefreshImageView.isSelected = defaultSwipeToRefresh
547 // Show the swipe-to-refresh text view.
548 swipeToRefreshTextView.visibility = View.VISIBLE
550 // Set the background color to be transparent.
551 swipeToRefreshLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
554 DomainsDatabaseHelper.ENABLED -> {
555 // Set the icon color.
556 swipeToRefreshImageView.isSelected = true
558 // Hide the swipe-to-refresh text view.
559 swipeToRefreshTextView.visibility = View.GONE
561 // Set the background color to be blue.
562 swipeToRefreshLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
565 DomainsDatabaseHelper.DISABLED -> {
566 // Set the icon color.
567 swipeToRefreshImageView.isSelected = false
569 // Hide the swipe-to-refresh text view.
570 swipeToRefreshTextView.visibility = View.GONE
572 // Set the background color to be blue.
573 swipeToRefreshLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
577 // Get the WebView theme string arrays.
578 val webViewThemeStringArray = resources.getStringArray(R.array.webview_theme_array)
579 val webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values)
581 // Get the WebView theme entry number that matches the current WebView theme.
582 val appWebViewThemeEntryNumber = when (defaultWebViewTheme) {
583 webViewThemeEntryValuesStringArray[1] -> { 1 } // The light theme is selected.
584 webViewThemeEntryValuesStringArray[2] -> { 2 } // The dark theme is selected.
585 else -> { 0 } // The system default theme is selected.
588 // Select the WebView theme in the spinner.
589 webViewThemeSpinner.setSelection(webViewThemeInt)
591 // Set the WebView theme text.
592 if (appWebViewThemeEntryNumber == DomainsDatabaseHelper.SYSTEM_DEFAULT) { // The app WebView theme is system default.
593 // Set the text according to the current UI theme.
594 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
595 webViewThemeTextView.text = webViewThemeStringArray[DomainsDatabaseHelper.LIGHT_THEME]
597 webViewThemeTextView.text = webViewThemeStringArray[DomainsDatabaseHelper.DARK_THEME]
598 } else { // The app WebView theme is not system default.
599 // Set the text according to the app WebView theme.
600 webViewThemeTextView.text = webViewThemeStringArray[appWebViewThemeEntryNumber]
603 // Set the WebView theme icon and text visibility.
604 when (webViewThemeInt) {
605 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
606 // Set the icon color.
607 when (appWebViewThemeEntryNumber) {
608 DomainsDatabaseHelper.SYSTEM_DEFAULT -> webViewThemeImageView.isSelected = (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
609 DomainsDatabaseHelper.LIGHT_THEME -> webViewThemeImageView.isSelected = true
610 DomainsDatabaseHelper.DARK_THEME -> webViewThemeImageView.isSelected = false
613 // Show the WebView theme text view.
614 webViewThemeTextView.visibility = View.VISIBLE
616 // Set the background color to be transparent.
617 webViewThemeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
620 DomainsDatabaseHelper.LIGHT_THEME -> {
621 // Set the icon color.
622 webViewThemeImageView.isSelected = true
624 // Hide the WebView theme text view.
625 webViewThemeTextView.visibility = View.GONE
627 // Set the background color to be blue.
628 webViewThemeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
631 DomainsDatabaseHelper.DARK_THEME -> {
632 // Set the icon color.
633 webViewThemeImageView.isSelected = false
635 // Hide the WebView theme text view.
636 webViewThemeTextView.visibility = View.GONE
638 // Set the background color to be blue.
639 webViewThemeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
643 // Select the wide viewport in the spinner.
644 wideViewportSpinner.setSelection(wideViewportInt)
646 // Set the default wide viewport text.
647 if (defaultWideViewport)
648 wideViewportTextView.text = wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
650 wideViewportTextView.text = wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
652 // Set the wide viewport icon and text view settings.
653 when (wideViewportInt) {
654 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
655 // Set the icon color.
656 wideViewportImageView.isSelected = defaultWideViewport
658 // Show the wide viewport text view.
659 wideViewportTextView.visibility = View.VISIBLE
661 // Set the background color to be transparent.
662 wideViewportLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
665 DomainsDatabaseHelper.ENABLED -> {
666 // Set the icon color.
667 wideViewportImageView.isSelected = true
669 // Hide the wide viewport text view.
670 wideViewportTextView.visibility = View.GONE
672 // Set the background color to be blue.
673 wideViewportLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
676 DomainsDatabaseHelper.DISABLED -> {
677 // Set the icon color.
678 wideViewportImageView.isSelected = false
680 // Hide the wide viewport text view.
681 wideViewportTextView.visibility = View.GONE
683 // Set the background color to be blue.
684 wideViewportLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
688 // Display the website images mode in the spinner.
689 displayImagesSpinner.setSelection(displayImagesInt)
691 // Set the default display images text.
692 if (defaultDisplayWebpageImages)
693 displayImagesTextView.text = displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED)
695 displayImagesTextView.text = displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED)
697 // Set the display website images icon and text view settings.
698 when (displayImagesInt) {
699 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
700 // Set the icon color.
701 displayImagesImageView.isSelected = defaultDisplayWebpageImages
703 // Show the display images text view.
704 displayImagesTextView.visibility = View.VISIBLE
706 // Set the background color to be transparent.
707 displayImagesLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
710 DomainsDatabaseHelper.ENABLED -> {
711 // Set the icon color.
712 displayImagesImageView.isSelected = true
714 // Hide the display images text view.
715 displayImagesTextView.visibility = View.GONE
717 // Set the background color to be blue.
718 displayImagesLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
721 DomainsDatabaseHelper.DISABLED -> {
722 // Set the icon color.
723 displayImagesImageView.isSelected = false
725 // Hide the display images text view.
726 displayImagesTextView.visibility = View.GONE
728 // Set the background color to be blue.
729 displayImagesLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
733 // Store the current date.
734 val currentDate = Calendar.getInstance().time
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 savedSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, savedSslIssuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
738 savedSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, savedSslIssuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
739 savedSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslIssuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
740 savedSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, savedSslIssuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
741 savedSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, savedSslIssuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
743 // Check the certificate Common Name against the domain name.
744 val savedSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslIssuedToCNameString)
746 // Format the issued to Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
747 if (savedSslCommonNameMatchesDomainName)
748 savedSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, savedSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
750 savedSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, savedSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
752 // Format the start date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
753 if (savedSslStartDate != null && savedSslStartDate.after(currentDate)) // The certificate start date is in the future.
754 savedSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, savedSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
755 else // The certificate start date is in the past.
756 savedSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, savedSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
758 // Format the end date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
759 if (savedSslEndDate != null && savedSslEndDate.before(currentDate)) // The certificate end date is in the past.
760 savedSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, savedSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
761 else // The certificate end date is in the future.
762 savedSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, savedSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
764 // Display the saved website SSL certificate strings.
765 savedSslIssuedToCNameTextView.text = savedSslIssuedToCNameStringBuilder
766 savedSslIssuedToONameTextView.text = savedSslIssuedToONameStringBuilder
767 savedSslIssuedToUNameTextView.text = savedSslIssuedToUNameStringBuilder
768 savedSslIssuedByCNameTextView.text = savedSslIssuedByCNameStringBuilder
769 savedSslIssuedByONameTextView.text = savedSslIssuedByONameStringBuilder
770 savedSslIssuedByUNameTextView.text = savedSslIssuedByUNameStringBuilder
771 savedSslStartDateTextView.text = savedSslStartDateStringBuilder
772 savedSslEndDateTextView.text = savedSslEndDateStringBuilder
774 // Populate the current website SSL certificate if there is one.
775 if (DomainsActivity.sslIssuedToCName != null) {
776 // Get dates from the raw long values.
777 val currentSslStartDate = Date(DomainsActivity.sslStartDateLong)
778 val currentSslEndDate = Date(DomainsActivity.sslEndDateLong)
780 // Create a spannable string builder for each text view that needs multiple colors of text.
781 val currentSslIssuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName)
782 val currentSslIssuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedToOName)
783 val currentSslIssuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedToUName)
784 val currentSslIssuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedByCName)
785 val currentSslIssuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedByOName)
786 val currentSslIssuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedByUName)
787 val currentSslStartDateStringBuilder = SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslStartDate))
788 val currentSslEndDateStringBuilder = SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslEndDate))
790 // Setup the string builders to display the general certificate information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
791 currentSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, currentSslIssuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
792 currentSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, currentSslIssuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
793 currentSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslIssuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
794 currentSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, currentSslIssuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
795 currentSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, currentSslIssuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
797 // Check the certificate Common Name against the domain name.
798 val currentSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, DomainsActivity.sslIssuedToCName)
800 // Format the issued to Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
801 if (currentSslCommonNameMatchesDomainName)
802 currentSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, currentSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
804 currentSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, currentSslIssuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
806 // Format the start date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
807 if (currentSslStartDate.after(currentDate)) // The certificate start date is in the future.
808 currentSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, currentSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
809 else // The certificate start date is in the past.
810 currentSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, currentSslStartDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
812 // Format the end date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
813 if (currentSslEndDate.before(currentDate)) // The certificate end date is in the past.
814 currentSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, currentSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
815 else // The certificate end date is in the future.
816 currentSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, currentSslEndDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
818 // Display the current website SSL certificate strings.
819 currentSslIssuedToCNameTextView.text = currentSslIssuedToCNameStringBuilder
820 currentSslIssuedToONameTextView.text = currentSslIssuedToONameStringBuilder
821 currentSslIssuedToUNameTextView.text = currentSslIssuedToUNameStringBuilder
822 currentSslIssuedByCNameTextView.text = currentSslIssuedByCNameStringBuilder
823 currentSslIssuedByONameTextView.text = currentSslIssuedByONameStringBuilder
824 currentSslIssuedByUNameTextView.text = currentSslIssuedByUNameStringBuilder
825 currentSslStartDateTextView.text = currentSslStartDateStringBuilder
826 currentSslEndDateTextView.text = currentSslEndDateStringBuilder
829 // Set the initial display status of the SSL certificates card views.
830 if (pinnedSslCertificateSwitch.isChecked) { // An SSL certificate is pinned.
831 // Set the visibility of the saved SSL certificate.
832 if (savedSslIssuedToCNameString == null)
833 savedSslCardView.visibility = View.GONE
835 savedSslCardView.visibility = View.VISIBLE
837 // Set the visibility of the current website SSL certificate.
838 if (DomainsActivity.sslIssuedToCName == null) { // There is no current SSL certificate.
839 // Hide the SSL certificate.
840 currentSslCardView.visibility = View.GONE
842 // Show the instruction.
843 noCurrentWebsiteCertificateTextView.visibility = View.VISIBLE
844 } else { // There is a current SSL certificate.
845 // Show the SSL certificate.
846 currentSslCardView.visibility = View.VISIBLE
848 // Hide the instruction.
849 noCurrentWebsiteCertificateTextView.visibility = View.GONE
852 // Set the status of the radio buttons and the card view backgrounds.
853 if (savedSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is displayed.
854 // Check the saved SSL certificate radio button.
855 savedSslCertificateRadioButton.isChecked = true
857 // Uncheck the current website SSL certificate radio button.
858 currentWebsiteCertificateRadioButton.isChecked = false
860 // Darken the background of the current website SSL certificate linear layout.
861 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
862 } else if (currentSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is hidden but the current website SSL certificate is visible.
863 // Check the current website SSL certificate radio button.
864 currentWebsiteCertificateRadioButton.isChecked = true
866 // Uncheck the saved SSL certificate radio button.
867 savedSslCertificateRadioButton.isChecked = false
868 } else { // Neither SSL certificate is visible.
869 // Uncheck both radio buttons.
870 savedSslCertificateRadioButton.isChecked = false
871 currentWebsiteCertificateRadioButton.isChecked = false
873 } else { // An SSL certificate is not pinned.
874 // Hide the SSl certificates and instructions.
875 savedSslCardView.visibility = View.GONE
876 currentSslCardView.visibility = View.GONE
877 noCurrentWebsiteCertificateTextView.visibility = View.GONE
879 // Uncheck the radio buttons.
880 savedSslCertificateRadioButton.isChecked = false
881 currentWebsiteCertificateRadioButton.isChecked = false
884 // Populate the saved and current IP addresses.
885 savedIpAddressesTextView.text = savedIpAddresses
886 currentIpAddressesTextView.text = DomainsActivity.currentIpAddresses
888 // Set the initial display status of the IP addresses card views.
889 if (pinnedIpAddressesSwitch.isChecked) { // IP addresses are pinned.
890 // Set the visibility of the saved IP addresses.
891 if (savedIpAddresses == null) // There are no saved IP addresses.
892 savedIpAddressesCardView.visibility = View.GONE
893 else // There are saved IP addresses.
894 savedIpAddressesCardView.visibility = View.VISIBLE
896 // Set the visibility of the current IP addresses.
897 currentIpAddressesCardView.visibility = View.VISIBLE
899 // Set the status of the radio buttons and the card view backgrounds.
900 if (savedIpAddressesCardView.visibility == View.VISIBLE) { // The saved IP addresses are displayed.
901 // Check the saved IP addresses radio button.
902 savedIpAddressesRadioButton.isChecked = true
904 // Uncheck the current IP addresses radio button.
905 currentIpAddressesRadioButton.isChecked = false
907 // Darken the background of the current IP addresses linear layout.
908 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
909 } else { // The saved IP addresses are hidden.
910 // Check the current IP addresses radio button.
911 currentIpAddressesRadioButton.isChecked = true
913 // Uncheck the saved IP addresses radio button.
914 savedIpAddressesRadioButton.isChecked = false
916 } else { // IP addresses are not pinned.
917 // Hide the IP addresses card views.
918 savedIpAddressesCardView.visibility = View.GONE
919 currentIpAddressesCardView.visibility = View.GONE
921 // Uncheck the radio buttons.
922 savedIpAddressesRadioButton.isChecked = false
923 currentIpAddressesRadioButton.isChecked = false
927 // Set the JavaScript switch listener.
928 javaScriptSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
929 // Update the JavaScript icon.
931 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null))
933 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null))
935 // Set the DOM storage switch status.
936 domStorageSwitch.isEnabled = isChecked
938 // Set the DOM storage ghosted icon status.
939 domStorageImageView.isEnabled = isChecked
942 // Set the cookies switch listener.
943 cookiesSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
944 // Update the icon color.
945 cookiesImageView.isSelected = isChecked
948 // Set the DOM Storage switch listener.
949 domStorageSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
950 // Update the icon color.
951 domStorageImageView.isSelected = isChecked
954 // Set the form data switch listener. It can be removed once the minimum API >= 26.
955 if (Build.VERSION.SDK_INT < 26) {
956 formDataSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
957 // Update the icon color.
958 formDataImageView.isSelected = isChecked
962 // Set the EasyList switch listener.
963 easyListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
964 // Update the icon color.
965 easyListImageView.isSelected = isChecked
968 // Set the EasyPrivacy switch listener.
969 easyPrivacySwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
970 // Update the icon color.
971 easyPrivacyImageView.isSelected = isChecked
974 // Set the Fanboy's Annoyance List switch listener.
975 fanboysAnnoyanceListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
976 // Update the icon color.
977 fanboysAnnoyanceListImageView.isSelected = isChecked
979 // Set Fanboy's Social Blocking List switch position.
980 fanboysSocialBlockingListSwitch.isEnabled = !isChecked
982 // Set the Social Blocking List icon ghosted status.
983 fanboysSocialBlockingListImageView.isEnabled = !isChecked
986 // Set the Fanboy's Social Blocking List switch listener.
987 fanboysSocialBlockingListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
988 // Update the icon color.
989 fanboysSocialBlockingListImageView.isSelected = isChecked
992 // Set the UltraList switch listener.
993 ultraListSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
994 // Update the icon color.
995 ultraListImageView.isSelected = isChecked
998 // Set the UltraPrivacy switch listener.
999 ultraPrivacySwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1000 // Update the icon color.
1001 ultraPrivacyImageView.isSelected = isChecked
1004 // Set the block all third-party requests switch listener.
1005 blockAllThirdPartyRequestsSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1006 // Update the icon color.
1007 blockAllThirdPartyRequestsImageView.isSelected = isChecked
1010 // Set the user agent spinner listener.
1011 userAgentSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1012 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1013 // Set the new user agent.
1015 DOMAINS_SYSTEM_DEFAULT_USER_AGENT -> {
1016 // Show the user agent text view.
1017 userAgentTextView.visibility = View.VISIBLE
1019 // Hide the custom user agent edit text.
1020 customUserAgentEditText.visibility = View.GONE
1022 // Set the user text.
1023 when (defaultUserAgentArrayPosition) {
1024 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
1025 UNRECOGNIZED_USER_AGENT -> userAgentTextView.text = defaultUserAgentName
1027 // Display the `WebView` default user agent.
1028 SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> userAgentTextView.text = webViewDefaultUserAgentString
1030 // Display the custom user agent.
1031 SETTINGS_CUSTOM_USER_AGENT -> userAgentTextView.text = defaultCustomUserAgentString
1033 // Get the user agent string from the user agent data array.
1034 else -> userAgentTextView.text = userAgentDataArray[defaultUserAgentArrayPosition]
1037 // Set the background color to be transparent.
1038 userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
1041 DOMAINS_WEBVIEW_DEFAULT_USER_AGENT -> {
1042 // Show the user agent text view.
1043 userAgentTextView.visibility = View.VISIBLE
1045 // Set the user agent text.
1046 userAgentTextView.text = webViewDefaultUserAgentString
1048 // Hide the custom user agent EditTex.
1049 customUserAgentEditText.visibility = View.GONE
1051 // Set the background color to be blue.
1052 userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1055 DOMAINS_CUSTOM_USER_AGENT -> {
1056 // Hide the user agent TextView.
1057 userAgentTextView.visibility = View.GONE
1059 // Show the custom user agent edit text.
1060 customUserAgentEditText.visibility = View.VISIBLE
1062 // Set the current user agent name as the text.
1063 customUserAgentEditText.setText(currentUserAgentName)
1065 // Set the background color to be blue.
1066 userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1070 // Show the user agent text view.
1071 userAgentTextView.visibility = View.VISIBLE
1073 // Set the text from the user agent data array, which has one less entry than the spinner, so the position must be decremented.
1074 userAgentTextView.text = userAgentDataArray[position - 1]
1076 // Hide the custom user agent edit text.
1077 customUserAgentEditText.visibility = View.GONE
1079 // Set the background color to be blue.
1080 userAgentLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1085 override fun onNothingSelected(parent: AdapterView<*>?) {
1090 // Set the font size spinner listener.
1091 fontSizeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1092 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1093 // Update the font size display options.
1094 if (position == 0) { // The system default font size has been selected.
1095 // Show the default font size text view.
1096 defaultFontSizeTextView.visibility = View.VISIBLE
1098 // Hide the custom font size edit text.
1099 customFontSizeEditText.visibility = View.GONE
1101 // Set the background color to be transparent.
1102 fontSizeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
1103 } else { // A custom font size has been selected.
1104 // Hide the default font size text view.
1105 defaultFontSizeTextView.visibility = View.GONE
1107 // Show the custom font size edit text.
1108 customFontSizeEditText.visibility = View.VISIBLE
1110 // Set the background color to be blue.
1111 fontSizeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1115 override fun onNothingSelected(parent: AdapterView<*>?) {
1120 // Set the swipe-to-refresh spinner listener.
1121 swipeToRefreshSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1122 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1123 // Update the icon and the visibility of the text view.
1125 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1126 // Set the icon color.
1127 swipeToRefreshImageView.isSelected = defaultSwipeToRefresh
1129 // Show the swipe-to-refresh text view.
1130 swipeToRefreshTextView.visibility = View.VISIBLE
1132 // Set the background color to be transparent.
1133 swipeToRefreshLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
1136 DomainsDatabaseHelper.ENABLED -> {
1137 // Set the icon color.
1138 swipeToRefreshImageView.isSelected = true
1140 // Hide the swipe-to-refresh text view.
1141 swipeToRefreshTextView.visibility = View.GONE
1143 // Set the background color to be blue.
1144 swipeToRefreshLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1147 DomainsDatabaseHelper.DISABLED -> {
1148 // Set the icon color.
1149 swipeToRefreshImageView.isSelected = false
1151 // Hide the swipe-to-refresh text view.
1152 swipeToRefreshTextView.visibility = View.GONE
1154 // Set the background color to be blue.
1155 swipeToRefreshLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1160 override fun onNothingSelected(parent: AdapterView<*>?) {
1165 // Set the WebView theme spinner listener.
1166 webViewThemeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1167 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1168 // Update the icon and the visibility of the WebView theme text view.
1170 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1171 // Set the icon color.
1172 when (appWebViewThemeEntryNumber) {
1173 DomainsDatabaseHelper.SYSTEM_DEFAULT -> webViewThemeImageView.isSelected = (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO)
1174 DomainsDatabaseHelper.LIGHT_THEME -> webViewThemeImageView.isSelected = true
1175 DomainsDatabaseHelper.DARK_THEME -> webViewThemeImageView.isSelected = false
1178 // Show the WebView theme text view.
1179 webViewThemeTextView.visibility = View.VISIBLE
1181 // Set the background color to be transparent.
1182 webViewThemeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
1185 DomainsDatabaseHelper.LIGHT_THEME -> {
1186 // Set the icon color.
1187 webViewThemeImageView.isSelected = true
1189 // Hide the WebView theme text view.
1190 webViewThemeTextView.visibility = View.GONE
1192 // Set the background color to be blue.
1193 webViewThemeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1196 DomainsDatabaseHelper.DARK_THEME -> {
1197 // Set the icon color.
1198 webViewThemeImageView.isSelected = false
1200 // Hide the WebView theme text view.
1201 webViewThemeTextView.visibility = View.GONE
1203 // Set the background color to be blue.
1204 webViewThemeLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1209 override fun onNothingSelected(parent: AdapterView<*>?) {
1214 // Set the wide viewport spinner listener.
1215 wideViewportSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1216 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1217 // Update the icon and the visibility of the wide viewport text view.
1219 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1220 // Set the icon color.
1221 wideViewportImageView.isSelected = defaultWideViewport
1223 // Show the wide viewport text view.
1224 wideViewportTextView.visibility = View.VISIBLE
1226 // Set the background color to be transparent.
1227 wideViewportLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
1230 DomainsDatabaseHelper.ENABLED -> {
1231 // Set the icon color.
1232 wideViewportImageView.isSelected = true
1234 // Hide the wide viewport text view.
1235 wideViewportTextView.visibility = View.GONE
1237 // Set the background color to be blue.
1238 wideViewportLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1241 DomainsDatabaseHelper.DISABLED -> {
1242 // Set the icon color.
1243 wideViewportImageView.isSelected = false
1245 // Hid ethe wide viewport text view.
1246 wideViewportTextView.visibility = View.GONE
1248 // Set the background color to be blue.
1249 wideViewportLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1254 override fun onNothingSelected(parent: AdapterView<*>?) {
1259 // Set the display webpage images spinner listener.
1260 displayImagesSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
1261 override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
1262 // Update the icon and the visibility of the display images text view.
1264 DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
1265 // Set the icon color.
1266 displayImagesImageView.isSelected = defaultDisplayWebpageImages
1268 // Show the display images text view.
1269 displayImagesTextView.visibility = View.VISIBLE
1271 // Set the background color to be transparent.
1272 displayImagesLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.transparent))
1275 DomainsDatabaseHelper.ENABLED -> {
1276 // Set the icon color.
1277 displayImagesImageView.isSelected = true
1279 // Hide the display images text view.
1280 displayImagesTextView.visibility = View.GONE
1282 // Set the background color to be blue.
1283 displayImagesLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1286 DomainsDatabaseHelper.DISABLED -> {
1287 // Set the icon color.
1288 displayImagesImageView.isSelected = false
1290 // Hide the display images text view.
1291 displayImagesTextView.visibility = View.GONE
1293 // Set the background color to be blue.
1294 displayImagesLinearLayout.setBackgroundColor(getColor(requireContext(), R.color.blue_background))
1299 override fun onNothingSelected(parent: AdapterView<*>?) {
1304 // Set the pinned SSL certificate switch listener.
1305 pinnedSslCertificateSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1306 // Update the icon color.
1307 pinnedSslCertificateImageView.isSelected = isChecked
1309 // Update the views.
1310 if (isChecked) { // SSL certificate pinning is enabled.
1311 // Update the visibility of the saved SSL certificate.
1312 if (savedSslIssuedToCNameString == null)
1313 savedSslCardView.visibility = View.GONE
1315 savedSslCardView.visibility = View.VISIBLE
1317 // Update the visibility of the current website SSL certificate.
1318 if (DomainsActivity.sslIssuedToCName == null) {
1319 // Hide the SSL certificate.
1320 currentSslCardView.visibility = View.GONE
1322 // Show the instruction.
1323 noCurrentWebsiteCertificateTextView.visibility = View.VISIBLE
1325 // Show the SSL certificate.
1326 currentSslCardView.visibility = View.VISIBLE
1328 // Hide the instruction.
1329 noCurrentWebsiteCertificateTextView.visibility = View.GONE
1332 // Set the status of the radio buttons.
1333 if (savedSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is displayed.
1334 // Check the saved SSL certificate radio button.
1335 savedSslCertificateRadioButton.isChecked = true
1337 // Uncheck the current website SSL certificate radio button.
1338 currentWebsiteCertificateRadioButton.isChecked = false
1340 // Set the background of the saved SSL certificate linear layout to be transparent.
1341 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1343 // Darken the background of the current website SSL certificate linear layout according to the theme.
1344 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1346 // Scroll to the current website SSL certificate card.
1347 savedSslCardView.parent.requestChildFocus(savedSslCardView, savedSslCardView)
1348 } else if (currentSslCardView.visibility == View.VISIBLE) { // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1349 // Check the current website SSL certificate radio button.
1350 currentWebsiteCertificateRadioButton.isChecked = true
1352 // Uncheck the saved SSL certificate radio button.
1353 savedSslCertificateRadioButton.isChecked = false
1355 // Set the background of the current website SSL certificate linear layout to be transparent.
1356 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1358 // Darken the background of the saved SSL certificate linear layout according to the theme.
1359 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1361 // Scroll to the current website SSL certificate card.
1362 currentSslCardView.parent.requestChildFocus(currentSslCardView, currentSslCardView)
1363 } else { // Neither SSL certificate is visible.
1364 // Uncheck both radio buttons.
1365 savedSslCertificateRadioButton.isChecked = false
1366 currentWebsiteCertificateRadioButton.isChecked = false
1368 // Scroll to the current website SSL certificate card.
1369 noCurrentWebsiteCertificateTextView.parent.requestChildFocus(noCurrentWebsiteCertificateTextView, noCurrentWebsiteCertificateTextView)
1371 } else { // SSL certificate pinning is disabled.
1372 // Hide the SSl certificates and instructions.
1373 savedSslCardView.visibility = View.GONE
1374 currentSslCardView.visibility = View.GONE
1375 noCurrentWebsiteCertificateTextView.visibility = View.GONE
1377 // Uncheck the radio buttons.
1378 savedSslCertificateRadioButton.isChecked = false
1379 currentWebsiteCertificateRadioButton.isChecked = false
1383 // Set the saved SSL card view listener.
1384 savedSslCardView.setOnClickListener {
1385 // Check the saved SSL certificate radio button.
1386 savedSslCertificateRadioButton.isChecked = true
1388 // Uncheck the current website SSL certificate radio button.
1389 currentWebsiteCertificateRadioButton.isChecked = false
1391 // Set the background of the saved SSL certificate linear layout to be transparent.
1392 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1394 // Darken the background of the current website SSL certificate linear layout.
1395 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1398 // Set the saved SSL certificate radio button listener.
1399 savedSslCertificateRadioButton.setOnClickListener {
1400 // Check the saved SSL certificate radio button.
1401 savedSslCertificateRadioButton.isChecked = true
1403 // Uncheck the current website SSL certificate radio button.
1404 currentWebsiteCertificateRadioButton.isChecked = false
1406 // Set the background of the saved SSL certificate linear layout to be transparent.
1407 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1409 // Darken the background of the current website SSL certificate linear layout.
1410 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1413 // Set the current SSL card view listener.
1414 currentSslCardView.setOnClickListener {
1415 // Check the current website SSL certificate radio button.
1416 currentWebsiteCertificateRadioButton.isChecked = true
1418 // Uncheck the saved SSL certificate radio button.
1419 savedSslCertificateRadioButton.isChecked = false
1421 // Set the background of the current website SSL certificate linear layout to be transparent.
1422 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1424 // Darken the background of the saved SSL certificate linear layout.
1425 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1428 // Set the current website certificate radio button listener.
1429 currentWebsiteCertificateRadioButton.setOnClickListener {
1430 // Check the current website SSL certificate radio button.
1431 currentWebsiteCertificateRadioButton.isChecked = true
1433 // Uncheck the saved SSL certificate radio button.
1434 savedSslCertificateRadioButton.isChecked = false
1436 // Set the background of the current website SSL certificate linear layout to be transparent.
1437 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1439 // Darken the background of the saved SSL certificate linear layout.
1440 savedSslCertificateLinearLayout.setBackgroundResource(R.color.translucent_background)
1443 // Set the pinned IP addresses switch listener.
1444 pinnedIpAddressesSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
1445 // Update the icon color.
1446 pinnedIpAddressesImageView.isSelected = isChecked
1448 // Update the views.
1449 if (isChecked) { // IP addresses pinning is enabled.
1450 // Update the visibility of the saved IP addresses card view.
1451 if (savedIpAddresses == null)
1452 savedIpAddressesCardView.visibility = View.GONE
1454 savedIpAddressesCardView.visibility = View.VISIBLE
1456 // Show the current IP addresses card view.
1457 currentIpAddressesCardView.visibility = View.VISIBLE
1459 // Set the status of the radio buttons.
1460 if (savedIpAddressesCardView.visibility == View.VISIBLE) { // The saved IP addresses are visible.
1461 // Check the saved IP addresses radio button.
1462 savedIpAddressesRadioButton.isChecked = true
1464 // Uncheck the current IP addresses radio button.
1465 currentIpAddressesRadioButton.isChecked = false
1467 // Set the background of the saved IP addresses linear layout to be transparent.
1468 savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent)
1470 // Darken the background of the current IP addresses linear layout.
1471 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1472 } else { // The saved IP addresses are not visible.
1473 // Check the current IP addresses radio button.
1474 currentIpAddressesRadioButton.isChecked = true
1476 // Uncheck the saved IP addresses radio button.
1477 savedIpAddressesRadioButton.isChecked = false
1479 // Set the background of the current IP addresses linear layout to be transparent.
1480 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1482 // Darken the background of the saved IP addresses linear layout.
1483 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1486 // Scroll to the bottom of the card views.
1487 currentIpAddressesCardView.parent.requestChildFocus(currentIpAddressesCardView, currentIpAddressesCardView)
1488 } else { // IP addresses pinning is disabled.
1489 // Hide the IP addresses card views.
1490 savedIpAddressesCardView.visibility = View.GONE
1491 currentIpAddressesCardView.visibility = View.GONE
1493 // Uncheck the radio buttons.
1494 savedIpAddressesRadioButton.isChecked = false
1495 currentIpAddressesRadioButton.isChecked = false
1499 // Set the saved IP addresses card view listener.
1500 savedIpAddressesCardView.setOnClickListener {
1501 // Check the saved IP addresses radio button.
1502 savedIpAddressesRadioButton.isChecked = true
1504 // Uncheck the current website IP addresses radio button.
1505 currentIpAddressesRadioButton.isChecked = false
1507 // Set the background of the saved IP addresses linear layout to be transparent.
1508 savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1510 // Darken the background of the current IP addresses linear layout.
1511 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1514 // Set the saved IP addresses radio button listener.
1515 savedIpAddressesRadioButton.setOnClickListener {
1516 // Check the saved IP addresses radio button.
1517 savedIpAddressesRadioButton.isChecked = true
1519 // Uncheck the current website IP addresses radio button.
1520 currentIpAddressesRadioButton.isChecked = false
1522 // Set the background of the saved IP addresses linear layout to be transparent.
1523 savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1525 // Darken the background of the current IP addresses linear layout.
1526 currentIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1529 // Set the current IP addresses card view listener.
1530 currentIpAddressesCardView.setOnClickListener {
1531 // Check the current IP addresses radio button.
1532 currentIpAddressesRadioButton.isChecked = true
1534 // Uncheck the saved IP addresses radio button.
1535 savedIpAddressesRadioButton.isChecked = false
1537 // Set the background of the current IP addresses linear layout to be transparent.
1538 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1540 // Darken the background of the saved IP addresses linear layout.
1541 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1544 // Set the current IP addresses radio button listener.
1545 currentIpAddressesRadioButton.setOnClickListener {
1546 // Check the current IP addresses radio button.
1547 currentIpAddressesRadioButton.isChecked = true
1549 // Uncheck the saved IP addresses radio button.
1550 savedIpAddressesRadioButton.isChecked = false
1552 // Set the background of the current IP addresses linear layout to be transparent.
1553 currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent)
1555 // Darken the background of the saved IP addresses linear layout.
1556 savedIpAddressesLinearLayout.setBackgroundResource(R.color.translucent_background)
1559 // Set the scroll Y.
1560 domainSettingsScrollView.post { domainSettingsScrollView.scrollY = scrollY }
1562 // Return the domain settings view.
1563 return domainSettingsView
1566 private fun checkDomainNameAgainstCertificate(domainName: String?, certificateCommonName: String?): Boolean {
1567 // Initialize the domain names match tracker.
1568 var domainNamesMatch = false
1570 // Check various wildcard permutations if the domain name and the certificate Common Name are not empty.
1571 if ((domainName != null) && (certificateCommonName != null)) {
1572 // Check if the domains match.
1573 if (domainName == certificateCommonName)
1574 domainNamesMatch = true
1576 // If the domain name starts with a wildcard, check the base domain against all the subdomains of the certificate Common Name.
1577 if (!domainNamesMatch && domainName.startsWith("*.") && domainName.length > 2) {
1578 // Remove the initial `*.`.
1579 val baseDomainName = domainName.substring(2)
1581 // Create a copy of the certificate Common Name to test subdomains.
1582 var certificateCommonNameSubdomain: String = certificateCommonName
1584 // Check all the subdomains in the certificate Common Name subdomain against the base domain name.
1585 while (!domainNamesMatch && certificateCommonNameSubdomain.contains(".")) { // Stop checking if the domain names match or if there are no more dots.
1586 // Test the certificate Common Name subdomain against the base domain name.
1587 if (certificateCommonNameSubdomain == baseDomainName)
1588 domainNamesMatch = true
1590 // Strip out the lowest subdomain of the certificate Common Name subdomain.
1591 certificateCommonNameSubdomain = try {
1592 certificateCommonNameSubdomain.substring(certificateCommonNameSubdomain.indexOf(".") + 1)
1593 } catch (e: IndexOutOfBoundsException) { // The certificate Common Name subdomain ends with a dot.
1599 // If the certificate Common Name starts with a wildcard, check the base common name against all the subdomains of the domain name.
1600 if (!domainNamesMatch && certificateCommonName.startsWith("*.") && certificateCommonName.length > 2) {
1601 // Remove the initial `*.`.
1602 val baseCertificateCommonName = certificateCommonName.substring(2)
1604 // Setup a copy of domain name to test subdomains.
1605 var domainNameSubdomain: String = domainName
1607 // Check all the subdomains in the domain name subdomain against the base certificate Common Name.
1608 while (!domainNamesMatch && domainNameSubdomain.contains(".") && domainNameSubdomain.length > 2) {
1609 // Test the domain name subdomain against the base certificate Common Name.
1610 if (domainNameSubdomain == baseCertificateCommonName)
1611 domainNamesMatch = true
1613 // Strip out the lowest subdomain of the domain name subdomain.
1614 domainNameSubdomain = try {
1615 domainNameSubdomain.substring(domainNameSubdomain.indexOf(".") + 1)
1616 } catch (e: IndexOutOfBoundsException) { // `domainNameSubdomain` ends with a dot.
1622 // If both names start with a wildcard, check if the root of one contains the root of the other.
1623 if (!domainNamesMatch && domainName.startsWith("*.") && domainName.length > 2 && certificateCommonName.startsWith("*.") && certificateCommonName.length > 2) {
1624 // Remove the wildcards.
1625 val rootDomainName = domainName.substring(2)
1626 val rootCertificateCommonName = certificateCommonName.substring(2)
1628 // Check if one name ends with the contents of the other. If so, there will be overlap in the their wildcard subdomains.
1629 if (rootDomainName.endsWith(rootCertificateCommonName) || rootCertificateCommonName.endsWith(rootDomainName))
1630 domainNamesMatch = true
1634 return domainNamesMatch