9694778f05a400540581033f96eb82aa88bbe3f0
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / fragments / DomainSettingsFragment.java
1 /*
2  * Copyright © 2017-2019 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
5  *
6  * Privacy Browser 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.
10  *
11  * Privacy Browser 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.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package com.stoutner.privacybrowser.fragments;
21
22 import android.annotation.SuppressLint;
23 import android.content.Context;
24 import android.content.SharedPreferences;
25 import android.content.res.Resources;
26 import android.database.Cursor;
27 import android.os.Build;
28 import android.os.Bundle;
29 import android.preference.PreferenceManager;
30 import android.text.Editable;
31 import android.text.SpannableStringBuilder;
32 import android.text.Spanned;
33 import android.text.TextWatcher;
34 import android.text.style.ForegroundColorSpan;
35 import android.view.LayoutInflater;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.webkit.WebView;
39 import android.widget.AdapterView;
40 import android.widget.ArrayAdapter;
41 import android.widget.CompoundButton;
42 import android.widget.EditText;
43 import android.widget.ImageView;
44 import android.widget.LinearLayout;
45 import android.widget.RadioButton;
46 import android.widget.Spinner;
47 import android.widget.Switch;
48 import android.widget.TextView;
49
50 import androidx.annotation.NonNull;
51 import androidx.cardview.widget.CardView;
52 import androidx.fragment.app.Fragment;  // The AndroidX fragment must be used until minimum API >= 23.  Otherwise `getContext()` does not work.
53
54 import com.stoutner.privacybrowser.R;
55 import com.stoutner.privacybrowser.activities.DomainsActivity;
56 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
57 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
58
59 import java.text.DateFormat;
60 import java.util.Calendar;
61 import java.util.Date;
62
63 public class DomainSettingsFragment extends Fragment {
64     // `DATABASE_ID` is used by activities calling this fragment.
65     public static final String DATABASE_ID = "database_id";
66
67     // `databaseId` is public static so it can be accessed from `DomainsActivity`. It is also used in `onCreate()` and `onCreateView()`.
68     public static int databaseId;
69
70     @Override
71     public void onCreate(Bundle savedInstanceState) {
72         // Run the default commands.
73         super.onCreate(savedInstanceState);
74
75         // Remove the lint warning that `getArguments` might be null.
76         assert getArguments() != null;
77
78         // Store the database id in `databaseId`.
79         databaseId = getArguments().getInt(DATABASE_ID);
80     }
81
82     // The deprecated `getDrawable()` must be used until the minimum API >= 21.
83     @Override
84     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
85         // Inflate `domain_settings_fragment`.  `false` does not attach it to the root `container`.
86         View domainSettingsView = inflater.inflate(R.layout.domain_settings_fragment, container, false);
87
88         // Get a handle for the context and the resources.
89         Context context = getContext();
90         Resources resources = getResources();
91
92         // Get a handle for the shared preference.
93         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
94
95         // Store the default settings.
96         String defaultUserAgentName = sharedPreferences.getString("user_agent", getString(R.string.user_agent_default_value));
97         String defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value));
98         String defaultFontSizeString = sharedPreferences.getString("font_size", getString(R.string.font_size_default_value));
99         boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
100         boolean defaultNightMode = sharedPreferences.getBoolean("night_mode", false);
101         boolean defaultDisplayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true);
102         boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
103
104         // Get handles for the views in the fragment.
105         EditText domainNameEditText = domainSettingsView.findViewById(R.id.domain_settings_name_edittext);
106         Switch javaScriptEnabledSwitch = domainSettingsView.findViewById(R.id.javascript_switch);
107         ImageView javaScriptImageView = domainSettingsView.findViewById(R.id.javascript_imageview);
108         Switch firstPartyCookiesEnabledSwitch = domainSettingsView.findViewById(R.id.first_party_cookies_switch);
109         ImageView firstPartyCookiesImageView = domainSettingsView.findViewById(R.id.first_party_cookies_imageview);
110         LinearLayout thirdPartyCookiesLinearLayout = domainSettingsView.findViewById(R.id.third_party_cookies_linearlayout);
111         Switch thirdPartyCookiesEnabledSwitch = domainSettingsView.findViewById(R.id.third_party_cookies_switch);
112         ImageView thirdPartyCookiesImageView = domainSettingsView.findViewById(R.id.third_party_cookies_imageview);
113         Switch domStorageEnabledSwitch = domainSettingsView.findViewById(R.id.dom_storage_switch);
114         ImageView domStorageImageView = domainSettingsView.findViewById(R.id.dom_storage_imageview);
115         Switch formDataEnabledSwitch = domainSettingsView.findViewById(R.id.form_data_switch);  // The form data views can be remove once the minimum API >= 26.
116         ImageView formDataImageView = domainSettingsView.findViewById(R.id.form_data_imageview);  // The form data views can be remove once the minimum API >= 26.
117         Switch easyListSwitch = domainSettingsView.findViewById(R.id.easylist_switch);
118         ImageView easyListImageView = domainSettingsView.findViewById(R.id.easylist_imageview);
119         Switch easyPrivacySwitch = domainSettingsView.findViewById(R.id.easyprivacy_switch);
120         ImageView easyPrivacyImageView = domainSettingsView.findViewById(R.id.easyprivacy_imageview);
121         Switch fanboysAnnoyanceListSwitch = domainSettingsView.findViewById(R.id.fanboys_annoyance_list_switch);
122         ImageView fanboysAnnoyanceListImageView = domainSettingsView.findViewById(R.id.fanboys_annoyance_list_imageview);
123         Switch fanboysSocialBlockingListSwitch = domainSettingsView.findViewById(R.id.fanboys_social_blocking_list_switch);
124         ImageView fanboysSocialBlockingListImageView = domainSettingsView.findViewById(R.id.fanboys_social_blocking_list_imageview);
125         Switch ultraPrivacySwitch = domainSettingsView.findViewById(R.id.ultraprivacy_switch);
126         ImageView ultraPrivacyImageView = domainSettingsView.findViewById(R.id.ultraprivacy_imageview);
127         Switch blockAllThirdPartyRequestsSwitch = domainSettingsView.findViewById(R.id.block_all_third_party_requests_switch);
128         ImageView blockAllThirdPartyRequestsImageView = domainSettingsView.findViewById(R.id.block_all_third_party_requests_imageview);
129         Spinner userAgentSpinner = domainSettingsView.findViewById(R.id.user_agent_spinner);
130         TextView userAgentTextView = domainSettingsView.findViewById(R.id.user_agent_textview);
131         EditText customUserAgentEditText = domainSettingsView.findViewById(R.id.custom_user_agent_edittext);
132         Spinner fontSizeSpinner = domainSettingsView.findViewById(R.id.font_size_spinner);
133         TextView fontSizeTextView = domainSettingsView.findViewById(R.id.font_size_textview);
134         ImageView swipeToRefreshImageView = domainSettingsView.findViewById(R.id.swipe_to_refresh_imageview);
135         Spinner swipeToRefreshSpinner = domainSettingsView.findViewById(R.id.swipe_to_refresh_spinner);
136         TextView swipeToRefreshTextView = domainSettingsView.findViewById(R.id.swipe_to_refresh_textview);
137         ImageView nightModeImageView = domainSettingsView.findViewById(R.id.night_mode_imageview);
138         Spinner nightModeSpinner = domainSettingsView.findViewById(R.id.night_mode_spinner);
139         TextView nightModeTextView = domainSettingsView.findViewById(R.id.night_mode_textview);
140         ImageView displayWebpageImagesImageView = domainSettingsView.findViewById(R.id.display_webpage_images_imageview);
141         Spinner displayWebpageImagesSpinner = domainSettingsView.findViewById(R.id.display_webpage_images_spinner);
142         TextView displayImagesTextView = domainSettingsView.findViewById(R.id.display_webpage_images_textview);
143         ImageView pinnedSslCertificateImageView = domainSettingsView.findViewById(R.id.pinned_ssl_certificate_imageview);
144         Switch pinnedSslCertificateSwitch = domainSettingsView.findViewById(R.id.pinned_ssl_certificate_switch);
145         CardView savedSslCardView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_cardview);
146         LinearLayout savedSslCertificateLinearLayout = domainSettingsView.findViewById(R.id.saved_ssl_certificate_linearlayout);
147         RadioButton savedSslCertificateRadioButton = domainSettingsView.findViewById(R.id.saved_ssl_certificate_radiobutton);
148         TextView savedSslIssuedToCNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_cname);
149         TextView savedSslIssuedToONameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_oname);
150         TextView savedSslIssuedToUNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_uname);
151         TextView savedSslIssuedByCNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_cname);
152         TextView savedSslIssuedByONameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_oname);
153         TextView savedSslIssuedByUNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_uname);
154         TextView savedSslStartDateTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_start_date);
155         TextView savedSslEndDateTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_end_date);
156         CardView currentSslCardView = domainSettingsView.findViewById(R.id.current_website_certificate_cardview);
157         LinearLayout currentWebsiteCertificateLinearLayout = domainSettingsView.findViewById(R.id.current_website_certificate_linearlayout);
158         RadioButton currentWebsiteCertificateRadioButton = domainSettingsView.findViewById(R.id.current_website_certificate_radiobutton);
159         TextView currentSslIssuedToCNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_cname);
160         TextView currentSslIssuedToONameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_oname);
161         TextView currentSslIssuedToUNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_uname);
162         TextView currentSslIssuedByCNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_cname);
163         TextView currentSslIssuedByONameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_oname);
164         TextView currentSslIssuedByUNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_uname);
165         TextView currentSslStartDateTextView = domainSettingsView.findViewById(R.id.current_website_certificate_start_date);
166         TextView currentSslEndDateTextView = domainSettingsView.findViewById(R.id.current_website_certificate_end_date);
167         TextView noCurrentWebsiteCertificateTextView = domainSettingsView.findViewById(R.id.no_current_website_certificate);
168         ImageView pinnedIpAddressesImageView = domainSettingsView.findViewById(R.id.pinned_ip_addresses_imageview);
169         Switch pinnedIpAddressesSwitch = domainSettingsView.findViewById(R.id.pinned_ip_addresses_switch);
170         CardView savedIpAddressesCardView = domainSettingsView.findViewById(R.id.saved_ip_addresses_cardview);
171         LinearLayout savedIpAddressesLinearLayout = domainSettingsView.findViewById(R.id.saved_ip_addresses_linearlayout);
172         RadioButton savedIpAddressesRadioButton = domainSettingsView.findViewById(R.id.saved_ip_addresses_radiobutton);
173         TextView savedIpAddressesTextView = domainSettingsView.findViewById(R.id.saved_ip_addresses_textview);
174         CardView currentIpAddressesCardView = domainSettingsView.findViewById(R.id.current_ip_addresses_cardview);
175         LinearLayout currentIpAddressesLinearLayout = domainSettingsView.findViewById(R.id.current_ip_addresses_linearlayout);
176         RadioButton currentIpAddressesRadioButton = domainSettingsView.findViewById(R.id.current_ip_addresses_radiobutton);
177         TextView currentIpAddressesTextView = domainSettingsView.findViewById(R.id.current_ip_addresses_textview);
178
179         // Setup the pinned labels.
180         String cNameLabel = getString(R.string.common_name) + "  ";
181         String oNameLabel = getString(R.string.organization) + "  ";
182         String uNameLabel = getString(R.string.organizational_unit) + "  ";
183         String startDateLabel = getString(R.string.start_date) + "  ";
184         String endDateLabel = getString(R.string.end_date) + "  ";
185
186         // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
187         DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0);
188
189         // Get the database cursor for this ID and move it to the first row.
190         Cursor domainCursor = domainsDatabaseHelper.getCursorForId(databaseId);
191         domainCursor.moveToFirst();
192
193         // Save the cursor entries as variables.
194         String domainNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME));
195         int javaScriptEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT));
196         int firstPartyCookiesEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES));
197         int thirdPartyCookiesEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES));
198         int domStorageEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE));
199         int formDataEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA));  // Form data can be remove once the minimum API >= 26.
200         int easyListEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST));
201         int easyPrivacyEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY));
202         int fanboysAnnoyanceListInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST));
203         int fanboysSocialBlockingListInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST));
204         int ultraPrivacyEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY));
205         int blockAllThirdPartyRequestsInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS));
206         String currentUserAgentName = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
207         int fontSizeInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
208         int swipeToRefreshInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
209         int nightModeInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE));
210         int displayImagesInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
211         int pinnedSslCertificateInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE));
212         String savedSslIssuedToCNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME));
213         String savedSslIssuedToONameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION));
214         String savedSslIssuedToUNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT));
215         String savedSslIssuedByCNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME));
216         String savedSslIssuedByONameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION));
217         String savedSslIssuedByUNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT));
218         int pinnedIpAddressesInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES));
219         String savedIpAddresses = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES));
220
221         // Initialize the saved SSL certificate date variables.
222         Date savedSslStartDate = null;
223         Date savedSslEndDate = null;
224
225         // Only get the saved SSL certificate dates from the cursor if they are not set to `0`.
226         if (domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) != 0) {
227             savedSslStartDate = new Date(domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)));
228         }
229
230         if (domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) != 0) {
231             savedSslEndDate = new Date(domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)));
232         }
233
234         // Create array adapters for the spinners.
235         ArrayAdapter<CharSequence> translatedUserAgentArrayAdapter = ArrayAdapter.createFromResource(context, R.array.translated_domain_settings_user_agent_names, R.layout.spinner_item);
236         ArrayAdapter<CharSequence> fontSizeArrayAdapter = ArrayAdapter.createFromResource(context, R.array.domain_settings_font_size_entries, R.layout.spinner_item);
237         ArrayAdapter<CharSequence> fontSizeEntryValuesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.domain_settings_font_size_entry_values, R.layout.spinner_item);
238         ArrayAdapter<CharSequence> swipeToRefreshArrayAdapter = ArrayAdapter.createFromResource(context, R.array.swipe_to_refresh_array, R.layout.spinner_item);
239         ArrayAdapter<CharSequence> nightModeArrayAdapter = ArrayAdapter.createFromResource(context, R.array.night_mode_array, R.layout.spinner_item);
240         ArrayAdapter<CharSequence> displayImagesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.display_webpage_images_array, R.layout.spinner_item);
241
242         // Set the drop down view resource on the spinners.
243         translatedUserAgentArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
244         fontSizeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
245         swipeToRefreshArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
246         nightModeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
247         displayImagesArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
248
249         // Set the array adapters for the spinners.
250         userAgentSpinner.setAdapter(translatedUserAgentArrayAdapter);
251         fontSizeSpinner.setAdapter(fontSizeArrayAdapter);
252         swipeToRefreshSpinner.setAdapter(swipeToRefreshArrayAdapter);
253         nightModeSpinner.setAdapter(nightModeArrayAdapter);
254         displayWebpageImagesSpinner.setAdapter(displayImagesArrayAdapter);
255
256         // Create a spannable string builder for each TextView that needs multiple colors of text.
257         SpannableStringBuilder savedSslIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString);
258         SpannableStringBuilder savedSslIssuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + savedSslIssuedToONameString);
259         SpannableStringBuilder savedSslIssuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + savedSslIssuedToUNameString);
260         SpannableStringBuilder savedSslIssuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslIssuedByCNameString);
261         SpannableStringBuilder savedSslIssuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + savedSslIssuedByONameString);
262         SpannableStringBuilder savedSslIssuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + savedSslIssuedByUNameString);
263
264         // Initialize the spannable string builders for the saved SSL certificate dates.
265         SpannableStringBuilder savedSslStartDateStringBuilder;
266         SpannableStringBuilder savedSslEndDateStringBuilder;
267
268         // Leave the SSL certificate dates empty if they are `null`.
269         if (savedSslStartDate == null) {
270             savedSslStartDateStringBuilder = new SpannableStringBuilder(startDateLabel);
271         } else {
272             savedSslStartDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslStartDate));
273         }
274
275         if (savedSslEndDate == null) {
276             savedSslEndDateStringBuilder = new SpannableStringBuilder(endDateLabel);
277         } else {
278             savedSslEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslEndDate));
279         }
280
281         // Create a red foreground color span.  The deprecated `resources.getColor` must be used until the minimum API >= 23.
282         final ForegroundColorSpan redColorSpan = new ForegroundColorSpan(resources.getColor(R.color.red_a700));
283
284         // Create a blue foreground color span.
285         final ForegroundColorSpan blueColorSpan;
286
287         // Set the blue color span according to the theme.  The deprecated `resources.getColor` must be used until the minimum API >= 23.
288         if (darkTheme) {
289             blueColorSpan = new ForegroundColorSpan(resources.getColor(R.color.blue_400));
290         } else {
291             blueColorSpan = new ForegroundColorSpan(resources.getColor(R.color.blue_700));
292         }
293
294         // Set the domain name from the the database cursor.
295         domainNameEditText.setText(domainNameString);
296
297         // Update the certificates' `Common Name` color when the domain name text changes.
298         domainNameEditText.addTextChangedListener(new TextWatcher() {
299             @Override
300             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
301                 // Do nothing.
302             }
303
304             @Override
305             public void onTextChanged(CharSequence s, int start, int before, int count) {
306                 // Do nothing.
307             }
308
309             @Override
310             public void afterTextChanged(Editable s) {
311                 // Get the new domain name.
312                 String newDomainName = domainNameEditText.getText().toString();
313
314                 // Check the saved SSL certificate against the new domain name.
315                 boolean savedSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, savedSslIssuedToCNameString);
316
317                 // Create a `SpannableStringBuilder` for the saved certificate `Common Name`.
318                 SpannableStringBuilder savedSslCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString);
319
320                 // Format the saved certificate `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);
323                 } else {
324                     savedSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), savedSslCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
325                 }
326
327                 // Update the saved SSL issued to CName text view.
328                 savedSslIssuedToCNameTextView.setText(savedSslCNameStringBuilder);
329
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                     boolean currentSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, DomainsActivity.sslIssuedToCName);
334
335                     // Create a `SpannableStringBuilder` for the current website certificate `Common Name`.
336                     SpannableStringBuilder currentSslCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName);
337
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);
341                     } else {
342                         currentSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), currentSslCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
343                     }
344
345                     // Update the current SSL issued to CName text view.
346                     currentSslIssuedToCNameTextView.setText(currentSslCNameStringBuilder);
347                 }
348             }
349         });
350
351         // Create a boolean to track if night mode is enabled.
352         boolean nightModeEnabled = (nightModeInt == DomainsDatabaseHelper.NIGHT_MODE_ENABLED) || ((nightModeInt == DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT) && defaultNightMode);
353
354         // Disable the JavaScript switch if night mode is enabled.
355         if (nightModeEnabled) {
356             javaScriptEnabledSwitch.setEnabled(false);
357         } else {
358             javaScriptEnabledSwitch.setEnabled(true);
359         }
360
361         // Set the JavaScript icon.
362         if ((javaScriptEnabledInt == 1) || nightModeEnabled) {
363             javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled));
364         } else {
365             javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode));
366         }
367
368         // Set the JavaScript switch status.
369         if (javaScriptEnabledInt == 1) {  // JavaScript is enabled.
370             javaScriptEnabledSwitch.setChecked(true);
371         } else {  // JavaScript is disabled.
372             javaScriptEnabledSwitch.setChecked(false);
373         }
374
375         // Set the first-party cookies status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
376         if (firstPartyCookiesEnabledInt == 1) {  // First-party cookies are enabled.
377             firstPartyCookiesEnabledSwitch.setChecked(true);
378             firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_enabled));
379         } else {  // First-party cookies are disabled.
380             firstPartyCookiesEnabledSwitch.setChecked(false);
381
382             // Set the icon according to the theme.
383             if (darkTheme) {
384                 firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
385             } else {
386                 firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
387             }
388         }
389
390         // Only display third-party cookies if SDK_INT >= 21.
391         if (Build.VERSION.SDK_INT >= 21) {  // Third-party cookies can be configured for API >= 21.
392             // Only enable third-party-cookies if first-party cookies are enabled.
393             if (firstPartyCookiesEnabledInt == 1) {  // First-party cookies are enabled.
394                 // Set the third-party cookies status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
395                 if (thirdPartyCookiesEnabledInt == 1) {  // Both first-party and third-party cookies are enabled.
396                     thirdPartyCookiesEnabledSwitch.setChecked(true);
397                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
398                 } else {  // First party cookies are enabled but third-party cookies are disabled.
399                     thirdPartyCookiesEnabledSwitch.setChecked(false);
400
401                     // Set the icon according to the theme.
402                     if (darkTheme) {
403                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
404                     } else {
405                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
406                     }
407                 }
408             } else {  // First-party cookies are disabled.
409                 // Set the status of third-party cookies.
410                 if (thirdPartyCookiesEnabledInt == 1) {
411                     thirdPartyCookiesEnabledSwitch.setChecked(true);
412                 } else {
413                     thirdPartyCookiesEnabledSwitch.setChecked(false);
414                 }
415
416                 // Disable the third-party cookies switch.
417                 thirdPartyCookiesEnabledSwitch.setEnabled(false);
418
419                 // Set the icon according to the theme.
420                 if (darkTheme) {
421                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_dark));
422                 } else {
423                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_light));
424                 }
425             }
426         } else {  // Third-party cookies cannot be configured for API <= 21.
427             // Hide the LinearLayout for third-party cookies.
428             thirdPartyCookiesLinearLayout.setVisibility(View.GONE);
429         }
430
431         // Only enable DOM storage if JavaScript is enabled.
432         if ((javaScriptEnabledInt == 1) || nightModeEnabled) {  // JavaScript is enabled.
433             // Enable the DOM storage `Switch`.
434             domStorageEnabledSwitch.setEnabled(true);
435
436             // Set the DOM storage status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
437             if (domStorageEnabledInt == 1) {  // Both JavaScript and DOM storage are enabled.
438                 domStorageEnabledSwitch.setChecked(true);
439                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
440             } else {  // JavaScript is enabled but DOM storage is disabled.
441                 // Set the DOM storage switch to off.
442                 domStorageEnabledSwitch.setChecked(false);
443
444                 // Set the icon according to the theme.
445                 if (darkTheme) {
446                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
447                 } else {
448                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
449                 }
450             }
451         } else {  // JavaScript is disabled.
452             // Disable the DOM storage `Switch`.
453             domStorageEnabledSwitch.setEnabled(false);
454
455             // Set the checked status of DOM storage.
456             if (domStorageEnabledInt == 1) {  // DOM storage is enabled but JavaScript is disabled.
457                 domStorageEnabledSwitch.setChecked(true);
458             } else {  // Both JavaScript and DOM storage are disabled.
459                 domStorageEnabledSwitch.setChecked(false);
460             }
461
462             // Set the icon according to the theme.
463             if (darkTheme) {
464                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_dark));
465             } else {
466                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_light));
467             }
468         }
469
470         // Set the form data status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.  Form data can be removed once the minimum API >= 26.
471         if (Build.VERSION.SDK_INT >= 26) {  // Form data no longer applies to newer versions of Android.
472             // Hide the form data switch.
473             formDataEnabledSwitch.setVisibility(View.GONE);
474         } else {  // Form data should be displayed because this is an older version of Android.
475             if (formDataEnabledInt == 1) {  // Form data is on.
476                 formDataEnabledSwitch.setChecked(true);
477                 formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_enabled));
478             } else {  // Form data is off.
479                 // Turn the form data switch to off.
480                 formDataEnabledSwitch.setChecked(false);
481
482                 // Set the icon according to the theme.
483                 if (darkTheme) {
484                     formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_dark));
485                 } else {
486                     formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_light));
487                 }
488             }
489         }
490
491         // Set the EasyList status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
492         if (easyListEnabledInt == 1) {  // EasyList is on.
493             // Turn the switch on.
494             easyListSwitch.setChecked(true);
495
496             // Set the icon according to the theme.
497             if (darkTheme) {
498                 easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_dark));
499             } else {
500                 easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_light));
501             }
502         } else {  // EasyList is off.
503             // Turn the switch off.
504             easyListSwitch.setChecked(false);
505
506             // Set the icon according to the theme.
507             if (darkTheme) {
508                 easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_dark));
509             } else {
510                 easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_light));
511             }
512         }
513
514         // Set the EasyPrivacy status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
515         if (easyPrivacyEnabledInt == 1) {  // EasyPrivacy is on.
516             // Turn the switch on.
517             easyPrivacySwitch.setChecked(true);
518
519             // Set the icon according to the theme.
520             if (darkTheme) {
521                 easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_dark));
522             } else {
523                 easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_light));
524             }
525         } else {  // EasyPrivacy is off.
526             // Turn the switch off.
527             easyPrivacySwitch.setChecked(false);
528
529             // Set the icon according to the theme.
530             if (darkTheme) {
531                 easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_dark));
532             } else {
533                 easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_light));
534             }
535         }
536
537         // Set the Fanboy's Annoyance List status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
538         if (fanboysAnnoyanceListInt == 1) {  // Fanboy's Annoyance List is on.
539             // Turn the switch on.
540             fanboysAnnoyanceListSwitch.setChecked(true);
541
542             // Set the icon according to the theme.
543             if (darkTheme) {
544                 fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
545             } else {
546                 fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
547             }
548         } else {  // Fanboy's Annoyance List is off.
549             // Turn the switch off.
550             fanboysAnnoyanceListSwitch.setChecked(false);
551
552             // Set the icon according to the theme.
553             if (darkTheme) {
554                 fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
555             } else {
556                 fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
557             }
558         }
559
560         // Only enable Fanboy's Social Blocking List if Fanboy's Annoyance List is off.
561         if (fanboysAnnoyanceListInt == 0) {  // Fanboy's Annoyance List is on.
562             // Enable Fanboy's Social Blocking List.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
563             if (fanboysSocialBlockingListInt == 1) {  // Fanboy's Social Blocking List is on.
564                 // Enable the switch and turn it on.
565                 fanboysSocialBlockingListSwitch.setEnabled(true);
566                 fanboysSocialBlockingListSwitch.setChecked(true);
567
568                 // Set the icon according to the theme.
569                 if (darkTheme) {
570                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
571                 } else {
572                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
573                 }
574             } else {  // Fanboy's Social Blocking List is off.
575                 // Enable the switch but turn it off.
576                 fanboysSocialBlockingListSwitch.setEnabled(true);
577                 fanboysSocialBlockingListSwitch.setChecked(false);
578
579                 // Set the icon according to the theme.
580                 if (darkTheme) {
581                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
582                 } else {
583                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
584                 }
585             }
586         } else {  // Fanboy's Annoyance List is on.
587             // Disable Fanboy's Social Blocking List.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
588             if (fanboysSocialBlockingListInt == 1) {  // Fanboy's Social Blocking List is on.
589                 // Disable the switch but turn it on.
590                 fanboysSocialBlockingListSwitch.setEnabled(false);
591                 fanboysSocialBlockingListSwitch.setChecked(true);
592             } else {  // Fanboy's Social Blocking List is off.
593                 // Disable the switch and turn it off.
594                 fanboysSocialBlockingListSwitch.setEnabled(false);
595                 fanboysSocialBlockingListSwitch.setChecked(false);
596             }
597
598             // Set the icon according to the theme.
599             if (darkTheme) {
600                 fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_dark));
601             } else {
602                 fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_light));
603             }
604         }
605
606         // Set the UltraPrivacy status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
607         if (ultraPrivacyEnabledInt == 1) {  // UltraPrivacy is on.
608             // Turn the switch on.
609             ultraPrivacySwitch.setChecked(true);
610
611             // Set the icon according to the theme.
612             if (darkTheme) {
613                 ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_dark));
614             } else {
615                 ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_light));
616             }
617         } else {  // EasyPrivacy is off.
618             // Turn the switch off.
619             ultraPrivacySwitch.setChecked(false);
620
621             // Set the icon according to the theme.
622             if (darkTheme) {
623                 ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_dark));
624             } else {
625                 ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_light));
626             }
627         }
628
629         // Set the third-party resource blocking status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
630         if (blockAllThirdPartyRequestsInt == 1) {  // Blocking all third-party requests is on.
631             // Turn the switch on.
632             blockAllThirdPartyRequestsSwitch.setChecked(true);
633
634             // Set the icon according to the theme.
635             if (darkTheme) {
636                 blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_enabled_dark));
637             } else {
638                 blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_enabled_light));
639             }
640         } else {  // Blocking all third-party requests is off.
641             // Turn the switch off.
642             blockAllThirdPartyRequestsSwitch.setChecked(false);
643
644             // Set the icon according to the theme.
645             if (darkTheme) {
646                 blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_disabled_dark));
647             } else {
648                 blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_disabled_light));
649             }
650         }
651
652         // Inflated a WebView to get the default user agent.
653         // `@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.
654         @SuppressLint("InflateParams") View bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false);
655         WebView bareWebView = bareWebViewLayout.findViewById(R.id.bare_webview);
656         final String webViewDefaultUserAgentString = bareWebView.getSettings().getUserAgentString();
657
658         // Get a handle for the user agent array adapter.  This array does not contain the `System default` entry.
659         ArrayAdapter<CharSequence> userAgentNamesArray = ArrayAdapter.createFromResource(context, R.array.user_agent_names, R.layout.spinner_item);
660
661         // Get the positions of the user agent and the default user agent.
662         int userAgentArrayPosition = userAgentNamesArray.getPosition(currentUserAgentName);
663         int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
664
665         // Get a handle for the user agent data array.  This array does not contain the `System default` entry.
666         String[] userAgentDataArray = resources.getStringArray(R.array.user_agent_data);
667
668         // Set the user agent text.
669         if (currentUserAgentName.equals(getString(R.string.system_default_user_agent))) {  // Use the system default user agent.
670             // Set the user agent according to the system default.
671             switch (defaultUserAgentArrayPosition) {
672                 case MainWebViewActivity.UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
673                     // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
674                     userAgentTextView.setText(defaultUserAgentName);
675                     break;
676
677                 case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
678                     // Display the `WebView` default user agent.
679                     userAgentTextView.setText(webViewDefaultUserAgentString);
680                     break;
681
682                 case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
683                     // Display the custom user agent.
684                     userAgentTextView.setText(defaultCustomUserAgentString);
685                     break;
686
687                 default:
688                     // Get the user agent string from the user agent data array.
689                     userAgentTextView.setText(userAgentDataArray[defaultUserAgentArrayPosition]);
690             }
691         } else if (userAgentArrayPosition == MainWebViewActivity.UNRECOGNIZED_USER_AGENT) {  // A custom user agent is stored in the current user agent name.
692             // Set the user agent spinner to `Custom user agent`.
693             userAgentSpinner.setSelection(MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT);
694
695             // Hide the user agent TextView.
696             userAgentTextView.setVisibility(View.GONE);
697
698             // Show the custom user agent EditText and set the current user agent name as the text.
699             customUserAgentEditText.setVisibility(View.VISIBLE);
700             customUserAgentEditText.setText(currentUserAgentName);
701         } else {  // The user agent name contains one of the canonical user agents.
702             // 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.
703             userAgentSpinner.setSelection(userAgentArrayPosition + 1);
704
705             // Show the user agent TextView.
706             userAgentTextView.setVisibility(View.VISIBLE);
707
708             // Hide the custom user agent EditText.
709             customUserAgentEditText.setVisibility(View.GONE);
710
711             // Set the user agent text.
712             if (userAgentArrayPosition == MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT) {  // The WebView default user agent is selected.
713                 // Display the WebView default user agent.
714                 userAgentTextView.setText(webViewDefaultUserAgentString);
715             } else {  // A user agent besides the default is selected.
716                 // 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.
717                 userAgentTextView.setText(userAgentDataArray[userAgentArrayPosition + 1]);
718             }
719         }
720
721         // Open the user agent spinner when the TextView is clicked.
722         userAgentTextView.setOnClickListener((View v) -> {
723             // Open the user agent spinner.
724             userAgentSpinner.performClick();
725         });
726
727         // Set the selected font size.
728         int fontSizeArrayPosition = fontSizeEntryValuesArrayAdapter.getPosition(String.valueOf(fontSizeInt));
729         fontSizeSpinner.setSelection(fontSizeArrayPosition);
730
731         // Set the default font size text.
732         int defaultFontSizeArrayPosition = fontSizeEntryValuesArrayAdapter.getPosition(defaultFontSizeString);
733         fontSizeTextView.setText(fontSizeArrayAdapter.getItem(defaultFontSizeArrayPosition));
734
735         // Set the display options for the font size TextView.
736         if (fontSizeArrayPosition == 0) {  // System default font size is selected.  Display `fontSizeTextView`.
737             fontSizeTextView.setVisibility(View.VISIBLE);
738         } else {  // A custom font size is specified.  Hide `fontSizeTextView`.
739             fontSizeTextView.setVisibility(View.GONE);
740         }
741
742         // Open the font size spinner when the TextView is clicked.
743         fontSizeTextView.setOnClickListener((View v) -> {
744             // Open the user agent spinner.
745             fontSizeSpinner.performClick();
746         });
747
748         // Display the swipe to refresh selection in the spinner.
749         swipeToRefreshSpinner.setSelection(swipeToRefreshInt);
750
751         // Set the swipe to refresh text.
752         if (defaultSwipeToRefresh) {
753             swipeToRefreshTextView.setText(swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.SWIPE_TO_REFRESH_ENABLED));
754         } else {
755             swipeToRefreshTextView.setText(swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.SWIPE_TO_REFRESH_DISABLED));
756         }
757
758         // Set the swipe to refresh icon and TextView settings.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
759         switch (swipeToRefreshInt) {
760             case DomainsDatabaseHelper.SWIPE_TO_REFRESH_SYSTEM_DEFAULT:
761                 if (defaultSwipeToRefresh) {  // Swipe to refresh is enabled by default.
762                     // Set the icon according to the theme.
763                     if (darkTheme) {
764                         swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_dark));
765                     } else {
766                         swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_light));
767                     }
768                 } else {  // Swipe to refresh is disabled by default
769                     // Set the icon according to the theme.
770                     if (darkTheme) {
771                         swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_dark));
772                     } else {
773                         swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_light));
774                     }
775                 }
776
777                 // Show the swipe to refresh TextView.
778                 swipeToRefreshTextView.setVisibility(View.VISIBLE);
779                 break;
780
781             case DomainsDatabaseHelper.SWIPE_TO_REFRESH_ENABLED:
782                 // Set the icon according to the theme.
783                 if (darkTheme) {
784                     swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_dark));
785                 } else {
786                     swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_light));
787                 }
788
789                 // Hide the swipe to refresh TextView.`
790                 swipeToRefreshTextView.setVisibility(View.GONE);
791                 break;
792
793             case DomainsDatabaseHelper.SWIPE_TO_REFRESH_DISABLED:
794                 // Set the icon according to the theme.
795                 if (darkTheme) {
796                     swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_dark));
797                 } else {
798                     swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_light));
799                 }
800
801                 // Hide the swipe to refresh TextView.
802                 swipeToRefreshTextView.setVisibility(View.GONE);
803         }
804
805         // Open the swipe to refresh spinner when the TextView is clicked.
806         swipeToRefreshTextView.setOnClickListener((View v) -> {
807             // Open the swipe to refresh spinner.
808             swipeToRefreshSpinner.performClick();
809         });
810
811         // Display the night mode in the spinner.
812         nightModeSpinner.setSelection(nightModeInt);
813
814         // Set the default night mode text.
815         if (defaultNightMode) {
816             nightModeTextView.setText(nightModeArrayAdapter.getItem(DomainsDatabaseHelper.NIGHT_MODE_ENABLED));
817         } else {
818             nightModeTextView.setText(nightModeArrayAdapter.getItem(DomainsDatabaseHelper.NIGHT_MODE_DISABLED));
819         }
820
821         // Set the night mode icon and TextView settings.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
822         switch (nightModeInt) {
823             case DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT:
824                 if (defaultNightMode) {  // Night mode enabled by default.
825                     // Set the icon according to the theme.
826                     if (darkTheme) {
827                         nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark));
828                     } else {
829                         nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light));
830                     }
831                 } else {  // Night mode disabled by default.
832                     // Set the icon according to the theme.
833                     if (darkTheme) {
834                         nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark));
835                     } else {
836                         nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light));
837                     }
838                 }
839
840                 // Show night mode TextView.
841                 nightModeTextView.setVisibility(View.VISIBLE);
842                 break;
843
844             case DomainsDatabaseHelper.NIGHT_MODE_ENABLED:
845                 // Set the icon according to the theme.
846                 if (darkTheme) {
847                     nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark));
848                 } else {
849                     nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light));
850                 }
851
852                 // Hide the night mode TextView.
853                 nightModeTextView.setVisibility(View.GONE);
854                 break;
855
856             case DomainsDatabaseHelper.NIGHT_MODE_DISABLED:
857                 // Set the icon according to the theme.
858                 if (darkTheme) {
859                     nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark));
860                 } else {
861                     nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light));
862                 }
863
864                 // Hide the night mode TextView.
865                 nightModeTextView.setVisibility(View.GONE);
866                 break;
867         }
868
869         // Open the night mode spinner when the TextView is clicked.
870         nightModeTextView.setOnClickListener((View v) -> {
871             // Open the night mode spinner.
872             nightModeSpinner.performClick();
873         });
874
875         // Display the website images mode in the spinner.
876         displayWebpageImagesSpinner.setSelection(displayImagesInt);
877
878         // Set the default display images text.
879         if (defaultDisplayWebpageImages) {
880             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED));
881         } else {
882             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED));
883         }
884
885         // Set the display website images icon and TextView settings.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
886         switch (displayImagesInt) {
887             case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_SYSTEM_DEFAULT:
888                 if (defaultDisplayWebpageImages) {  // Display webpage images enabled by default.
889                     // Set the icon according to the theme.
890                     if (darkTheme) {
891                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
892                     } else {
893                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
894                     }
895                 } else {  // Display webpage images disabled by default.
896                     // Set the icon according to the theme.
897                     if (darkTheme) {
898                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
899                     } else {
900                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
901                     }
902                 }
903
904                 // Show the display images TextView.
905                 displayImagesTextView.setVisibility(View.VISIBLE);
906                 break;
907
908             case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED:
909                 // Set the icon according to the theme.
910                 if (darkTheme) {
911                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
912                 } else {
913                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
914                 }
915
916                 // Hide the display images TextView.
917                 displayImagesTextView.setVisibility(View.GONE);
918                 break;
919
920             case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED:
921                 // Set the icon according to the theme.
922                 if (darkTheme) {
923                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
924                 } else {
925                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
926                 }
927
928                 // Hide the display images TextView.
929                 displayImagesTextView.setVisibility(View.GONE);
930                 break;
931         }
932
933         // Open the display images spinner when the TextView is clicked.
934         displayImagesTextView.setOnClickListener((View v) -> {
935             // Open the user agent spinner.
936             displayWebpageImagesSpinner.performClick();
937         });
938         
939         // Set the pinned SSL certificate icon.
940         if (pinnedSslCertificateInt == 1) {  // Pinned SSL certificate is enabled.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
941             // Check the switch.
942             pinnedSslCertificateSwitch.setChecked(true);
943
944             // Set the icon according to the theme.
945             if (darkTheme) {
946                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark));
947             } else {
948                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_light));
949             }
950         } else {  // Pinned SSL certificate is disabled.
951             // Uncheck the switch.
952             pinnedSslCertificateSwitch.setChecked(false);
953
954             // Set the icon according to the theme.
955             if (darkTheme) {
956                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark));
957             } else {
958                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_light));
959             }
960         }
961
962         // Store the current date.
963         Date currentDate = Calendar.getInstance().getTime();
964
965         // Setup the string builders to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
966         savedSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
967         savedSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
968         savedSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
969         savedSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
970         savedSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
971
972         // Check the certificate Common Name against the domain name.
973         boolean savedSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslIssuedToCNameString);
974
975         // Format the issued to Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
976         if (savedSslCommonNameMatchesDomainName) {
977             savedSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
978         } else {
979             savedSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), savedSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
980         }
981
982         //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
983         if ((savedSslStartDate != null) && savedSslStartDate.after(currentDate)) {  // The certificate start date is in the future.
984             savedSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), savedSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
985         } else {  // The certificate start date is in the past.
986             savedSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), savedSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
987         }
988
989         // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
990         if ((savedSslEndDate != null) && savedSslEndDate.before(currentDate)) {  // The certificate end date is in the past.
991             savedSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), savedSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
992         } else {  // The certificate end date is in the future.
993             savedSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), savedSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
994         }
995
996         // Display the saved website SSL certificate strings.
997         savedSslIssuedToCNameTextView.setText(savedSslIssuedToCNameStringBuilder);
998         savedSslIssuedToONameTextView.setText(savedSslIssuedToONameStringBuilder);
999         savedSslIssuedToUNameTextView.setText(savedSslIssuedToUNameStringBuilder);
1000         savedSslIssuedByCNameTextView.setText(savedSslIssuedByCNameStringBuilder);
1001         savedSslIssuedByONameTextView.setText(savedSslIssuedByONameStringBuilder);
1002         savedSslIssuedByUNameTextView.setText(savedSslIssuedByUNameStringBuilder);
1003         savedSslStartDateTextView.setText(savedSslStartDateStringBuilder);
1004         savedSslEndDateTextView.setText(savedSslEndDateStringBuilder);
1005
1006         // Populate the current website SSL certificate if there is one.
1007         if (DomainsActivity.sslIssuedToCName != null) {
1008             // Get dates from the raw long values.
1009             Date currentSslStartDate = new Date(DomainsActivity.sslStartDateLong);
1010             Date currentSslEndDate = new Date(DomainsActivity.sslEndDateLong);
1011
1012             // Create a spannable string builder for each text view that needs multiple colors of text.
1013             SpannableStringBuilder currentSslIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName);
1014             SpannableStringBuilder currentSslIssuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedToOName);
1015             SpannableStringBuilder currentSslIssuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedToUName);
1016             SpannableStringBuilder currentSslIssuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedByCName);
1017             SpannableStringBuilder currentSslIssuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedByOName);
1018             SpannableStringBuilder currentSslIssuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedByUName);
1019             SpannableStringBuilder currentSslStartDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
1020                     .format(currentSslStartDate));
1021             SpannableStringBuilder currentSslEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
1022                     .format(currentSslEndDate));
1023
1024             // Setup the string builders to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1025             currentSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentSslIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1026             currentSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentSslIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1027             currentSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1028             currentSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentSslIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1029             currentSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentSslIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1030
1031             // Check the certificate Common Name against the domain name.
1032             boolean currentSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, DomainsActivity.sslIssuedToCName);
1033
1034             // Format the issued to Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1035             if (currentSslCommonNameMatchesDomainName) {
1036                 currentSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1037             } else {
1038                 currentSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), currentSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1039             }
1040
1041             //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1042             if (currentSslStartDate.after(currentDate)) {  // The certificate start date is in the future.
1043                 currentSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), currentSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1044             } else {  // The certificate start date is in the past.
1045                 currentSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), currentSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1046             }
1047
1048             // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1049             if (currentSslEndDate.before(currentDate)) {  // The certificate end date is in the past.
1050                 currentSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), currentSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1051             } else {  // The certificate end date is in the future.
1052                 currentSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), currentSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1053             }
1054
1055             // Display the current website SSL certificate strings.
1056             currentSslIssuedToCNameTextView.setText(currentSslIssuedToCNameStringBuilder);
1057             currentSslIssuedToONameTextView.setText(currentSslIssuedToONameStringBuilder);
1058             currentSslIssuedToUNameTextView.setText(currentSslIssuedToUNameStringBuilder);
1059             currentSslIssuedByCNameTextView.setText(currentSslIssuedByCNameStringBuilder);
1060             currentSslIssuedByONameTextView.setText(currentSslIssuedByONameStringBuilder);
1061             currentSslIssuedByUNameTextView.setText(currentSslIssuedByUNameStringBuilder);
1062             currentSslStartDateTextView.setText(currentSslStartDateStringBuilder);
1063             currentSslEndDateTextView.setText(currentSslEndDateStringBuilder);
1064         }
1065
1066         // Set the initial display status of the SSL certificates card views.
1067         if (pinnedSslCertificateSwitch.isChecked()) {  // An SSL certificate is pinned.
1068             // Set the visibility of the saved SSL certificate.
1069             if (savedSslIssuedToCNameString == null) {
1070                 savedSslCardView.setVisibility(View.GONE);
1071             } else {
1072                 savedSslCardView.setVisibility(View.VISIBLE);
1073             }
1074
1075             // Set the visibility of the current website SSL certificate.
1076             if (DomainsActivity.sslIssuedToCName == null) {  // There is no current SSL certificate.
1077                 // Hide the SSL certificate.
1078                 currentSslCardView.setVisibility(View.GONE);
1079
1080                 // Show the instruction.
1081                 noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
1082             } else {  // There is a current SSL certificate.
1083                 // Show the SSL certificate.
1084                 currentSslCardView.setVisibility(View.VISIBLE);
1085
1086                 // Hide the instruction.
1087                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1088             }
1089
1090             // Set the status of the radio buttons and the card view backgrounds.
1091             if (savedSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
1092                 // Check the saved SSL certificate radio button.
1093                 savedSslCertificateRadioButton.setChecked(true);
1094
1095                 // Uncheck the current website SSL certificate radio button.
1096                 currentWebsiteCertificateRadioButton.setChecked(false);
1097
1098                 // Darken the background of the current website SSL certificate linear layout according to the theme.
1099                 if (darkTheme) {
1100                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1101                 } else {
1102                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1103                 }
1104             } else if (currentSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1105                 // Check the current website SSL certificate radio button.
1106                 currentWebsiteCertificateRadioButton.setChecked(true);
1107
1108                 // Uncheck the saved SSL certificate radio button.
1109                 savedSslCertificateRadioButton.setChecked(false);
1110             } else {  // Neither SSL certificate is visible.
1111                 // Uncheck both radio buttons.
1112                 savedSslCertificateRadioButton.setChecked(false);
1113                 currentWebsiteCertificateRadioButton.setChecked(false);
1114             }
1115         } else {  // An SSL certificate is not pinned.
1116             // Hide the SSl certificates and instructions.
1117             savedSslCardView.setVisibility(View.GONE);
1118             currentSslCardView.setVisibility(View.GONE);
1119             noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1120
1121             // Uncheck the radio buttons.
1122             savedSslCertificateRadioButton.setChecked(false);
1123             currentWebsiteCertificateRadioButton.setChecked(false);
1124         }
1125
1126         // Set the pinned IP addresses icon.
1127         if (pinnedIpAddressesInt == 1) {  // Pinned IP addresses is enabled.  Once the minimum API >= 21 a selector can be sued as the tint mode instead of specifying different icons.
1128             // Check the switch.
1129             pinnedIpAddressesSwitch.setChecked(true);
1130
1131             // Set the icon according to the theme.
1132             if (darkTheme) {
1133                 pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark));
1134             } else {
1135                 pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_light));
1136             }
1137         } else {  // Pinned IP Addresses is disabled.
1138             // Uncheck the switch.
1139             pinnedIpAddressesSwitch.setChecked(false);
1140
1141             // Set the icon according to the theme.
1142             if (darkTheme) {
1143                 pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark));
1144             } else {
1145                 pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_light));
1146             }
1147         }
1148
1149         // Populate the saved and current IP addresses.
1150         savedIpAddressesTextView.setText(savedIpAddresses);
1151         currentIpAddressesTextView.setText(DomainsActivity.currentIpAddresses);
1152
1153         // Set the initial display status of the IP addresses card views.
1154         if (pinnedIpAddressesSwitch.isChecked()) {  // IP addresses are pinned.
1155             // Set the visibility of the saved IP addresses.
1156             if (savedIpAddresses == null) {  // There are no saved IP addresses.
1157                 savedIpAddressesCardView.setVisibility(View.GONE);
1158             } else {  // There are saved IP addresses.
1159                 savedIpAddressesCardView.setVisibility(View.VISIBLE);
1160             }
1161
1162             // Set the visibility of the current IP addresses.
1163             currentIpAddressesCardView.setVisibility(View.VISIBLE);
1164
1165             // Set the status of the radio buttons and the card view backgrounds.
1166             if (savedIpAddressesCardView.getVisibility() == View.VISIBLE) {  // The saved IP addresses are displayed.
1167                 // Check the saved IP addresses radio button.
1168                 savedIpAddressesRadioButton.setChecked(true);
1169
1170                 // Uncheck the current IP addresses radio button.
1171                 currentIpAddressesRadioButton.setChecked(false);
1172
1173                 // Darken the background of the current IP addresses linear layout according to the theme.
1174                 if (darkTheme) {
1175                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1176                 } else {
1177                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1178                 }
1179             } else {  // The saved IP addresses are hidden.
1180                 // Check the current IP addresses radio button.
1181                 currentIpAddressesRadioButton.setChecked(true);
1182
1183                 // Uncheck the saved IP addresses radio button.
1184                 savedIpAddressesRadioButton.setChecked(false);
1185             }
1186         } else {  // IP addresses are not pinned.
1187             // Hide the IP addresses card views.
1188             savedIpAddressesCardView.setVisibility(View.GONE);
1189             currentIpAddressesCardView.setVisibility(View.GONE);
1190
1191             // Uncheck the radio buttons.
1192             savedIpAddressesRadioButton.setChecked(false);
1193             currentIpAddressesRadioButton.setChecked(false);
1194         }
1195
1196
1197         // Set the JavaScript switch listener.
1198         javaScriptEnabledSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1199             if (isChecked) {  // JavaScript is enabled.
1200                 // Update the JavaScript icon.
1201                 javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled));
1202
1203                 // Enable the DOM storage `Switch`.
1204                 domStorageEnabledSwitch.setEnabled(true);
1205
1206                 // Update the DOM storage icon.
1207                 if (domStorageEnabledSwitch.isChecked()) {  // DOM storage is enabled.
1208                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
1209                 } else {  // DOM storage is disabled.
1210                     // Set the icon according to the theme.
1211                     if (darkTheme) {
1212                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
1213                     } else {
1214                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
1215                     }
1216                 }
1217             } else {  // JavaScript is disabled.
1218                 // Update the JavaScript icon.
1219                 javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode));
1220
1221                 // Disable the DOM storage `Switch`.
1222                 domStorageEnabledSwitch.setEnabled(false);
1223
1224                 // Set the DOM storage icon according to the theme.
1225                 if (darkTheme) {
1226                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_dark));
1227                 } else {
1228                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_light));
1229                 }
1230             }
1231         });
1232
1233         // Set the first-party cookies switch listener.
1234         firstPartyCookiesEnabledSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1235             if (isChecked) {  // First-party cookies are enabled.
1236                 // Update the first-party cookies icon.
1237                 firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_enabled));
1238
1239                 // Enable the third-party cookies switch.
1240                 thirdPartyCookiesEnabledSwitch.setEnabled(true);
1241
1242                 // Update the third-party cookies icon.
1243                 if (thirdPartyCookiesEnabledSwitch.isChecked()) {  // Third-party cookies are enabled.
1244                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
1245                 } else {  // Third-party cookies are disabled.
1246                     // Set the third-party cookies icon according to the theme.
1247                     if (darkTheme) {
1248                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
1249                     } else {
1250                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
1251                     }
1252                 }
1253             } else {  // First-party cookies are disabled.
1254                 // Update the first-party cookies icon according to the theme.
1255                 if (darkTheme) {
1256                     firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
1257                 } else {
1258                     firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
1259                 }
1260
1261                 // Disable the third-party cookies switch.
1262                 thirdPartyCookiesEnabledSwitch.setEnabled(false);
1263
1264                 // Set the third-party cookies icon according to the theme.
1265                 if (darkTheme) {
1266                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_dark));
1267                 } else {
1268                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_light));
1269                 }
1270             }
1271         });
1272
1273         // Set the third-party cookies switch listener.
1274         thirdPartyCookiesEnabledSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1275             // Update the icon.
1276             if (isChecked) {
1277                 thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
1278             } else {
1279                 // Update the third-party cookies icon according to the theme.
1280                 if (darkTheme) {
1281                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
1282                 } else {
1283                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
1284                 }
1285             }
1286         });
1287
1288         // Set the DOM Storage switch listener.
1289         domStorageEnabledSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1290             // Update the icon.
1291             if (isChecked) {
1292                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
1293             } else {
1294                 // Set the icon according to the theme.
1295                 if (darkTheme) {
1296                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
1297                 } else {
1298                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
1299                 }
1300             }
1301         });
1302
1303         // Set the form data switch listener.  It can be removed once the minimum API >= 26.
1304         if (Build.VERSION.SDK_INT < 26) {
1305             formDataEnabledSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1306                 // Update the icon.
1307                 if (isChecked) {
1308                     formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_enabled));
1309                 } else {
1310                     // Set the icon according to the theme.
1311                     if (darkTheme) {
1312                         formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_dark));
1313                     } else {
1314                         formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_light));
1315                     }
1316                 }
1317             });
1318         }
1319
1320         // Set the EasyList switch listener.
1321         easyListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1322             // Update the icon.
1323             if (isChecked) {  // EasyList is on.
1324                 // Set the icon according to the theme.
1325                 if (darkTheme) {
1326                     easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_dark));
1327                 } else {
1328                     easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_light));
1329                 }
1330             } else {  // EasyList is off.
1331                 // Set the icon according to the theme.
1332                 if (darkTheme) {
1333                     easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_dark));
1334                 } else {
1335                     easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_light));
1336                 }
1337             }
1338         });
1339
1340         // Set the EasyPrivacy switch listener.
1341         easyPrivacySwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1342             // Update the icon.
1343             if (isChecked) {  // EasyPrivacy is on.
1344                 // Set the icon according to the theme.
1345                 if (darkTheme) {
1346                     easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_dark));
1347                 } else {
1348                     easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_light));
1349                 }
1350             } else {  // EasyPrivacy is off.
1351                 // Set the icon according to the theme.
1352                 if (darkTheme) {
1353                     easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_dark));
1354                 } else {
1355                     easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_light));
1356                 }
1357             }
1358         });
1359
1360         // Set the Fanboy's Annoyance List switch listener.
1361         fanboysAnnoyanceListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1362             // Update the icon and Fanboy's Social Blocking List.
1363             if (isChecked) {  // Fanboy's Annoyance List is on.
1364                 // Set the icon according to the theme.
1365                 if (darkTheme) {
1366                     fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
1367                 } else {
1368                     fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
1369                 }
1370
1371                 // Disable the Fanboy's Social Blocking List switch.
1372                 fanboysSocialBlockingListSwitch.setEnabled(false);
1373
1374                 // Update the Fanboy's Social Blocking List icon according to the theme.
1375                 if (darkTheme) {
1376                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_dark));
1377                 } else {
1378                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_light));
1379                 }
1380             } else {  // Fanboy's Annoyance List is off.
1381                 // Set the icon according to the theme.
1382                 if (darkTheme) {
1383                     fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
1384                 } else {
1385                     fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
1386                 }
1387
1388                 // Enable the Fanboy's Social Blocking List switch.
1389                 fanboysSocialBlockingListSwitch.setEnabled(true);
1390
1391                 // Update the Fanboy's Social Blocking List icon.
1392                 if (fanboysSocialBlockingListSwitch.isChecked()) {  // Fanboy's Social Blocking List is on.
1393                     // Update the icon according to the theme.
1394                     if (darkTheme) {
1395                         fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
1396                     } else {
1397                         fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
1398                     }
1399                 } else {  // Fanboy's Social Blocking List is off.
1400                     // Update the icon according to the theme.
1401                     if (darkTheme) {
1402                         fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
1403                     } else {
1404                         fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
1405                     }
1406                 }
1407             }
1408
1409         });
1410
1411         // Set the Fanboy's Social Blocking List switch listener.
1412         fanboysSocialBlockingListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1413             // Update the icon.
1414             if (isChecked) {  // Fanboy's Social Blocking List is on.
1415                 // Set the icon according to the theme.
1416                 if (darkTheme) {
1417                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
1418                 } else {
1419                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
1420                 }
1421             } else {  // Fanboy's Social Blocking List is off.
1422                 // Set the icon according to the theme.
1423                 if (darkTheme) {
1424                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
1425                 } else {
1426                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
1427                 }
1428             }
1429         });
1430
1431         // Set the UltraPrivacy switch listener.
1432         ultraPrivacySwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1433             // Update the icon.
1434             if (isChecked) {  // UltraPrivacy is on.
1435                 // Set the icon according to the theme.
1436                 if (darkTheme) {
1437                     ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_dark));
1438                 } else {
1439                     ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_light));
1440                 }
1441             } else {  // UltraPrivacy is off.
1442                 // Set the icon according to the theme.
1443                 if (darkTheme) {
1444                     ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_dark));
1445                 } else {
1446                     ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_light));
1447                 }
1448             }
1449         });
1450
1451         // Set the block all third-party requests switch listener.
1452         blockAllThirdPartyRequestsSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1453             // Update the icon.
1454             if (isChecked) {  // Blocking all third-party requests is on.
1455                 // Set the icon according to the theme.
1456                 if (darkTheme) {
1457                     blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_enabled_dark));
1458                 } else {
1459                     blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_enabled_light));
1460                 }
1461             } else {  // Blocking all third-party requests is off.
1462                 // Set the icon according to the theme.
1463                 if (darkTheme) {
1464                     blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_disabled_dark));
1465                 } else {
1466                     blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_disabled_light));
1467                 }
1468             }
1469         });
1470
1471         // Set the user agent spinner listener.
1472         userAgentSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1473             @Override
1474             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1475                 // Set the new user agent.
1476                 switch (position) {
1477                     case MainWebViewActivity.DOMAINS_SYSTEM_DEFAULT_USER_AGENT:
1478                         // Show the user agent TextView.
1479                         userAgentTextView.setVisibility(View.VISIBLE);
1480
1481                         // Hide the custom user agent EditText.
1482                         customUserAgentEditText.setVisibility(View.GONE);
1483
1484                         // Set the user text.
1485                         switch (defaultUserAgentArrayPosition) {
1486                             case MainWebViewActivity.UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
1487                                 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
1488                                 userAgentTextView.setText(defaultUserAgentName);
1489                                 break;
1490
1491                             case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
1492                                 // Display the `WebView` default user agent.
1493                                 userAgentTextView.setText(webViewDefaultUserAgentString);
1494                                 break;
1495
1496                             case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
1497                                 // Display the custom user agent.
1498                                 userAgentTextView.setText(defaultCustomUserAgentString);
1499                                 break;
1500
1501                             default:
1502                                 // Get the user agent string from the user agent data array.
1503                                 userAgentTextView.setText(userAgentDataArray[defaultUserAgentArrayPosition]);
1504                         }
1505                         break;
1506
1507                     case MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT:
1508                         // Show the user agent TextView and set the text.
1509                         userAgentTextView.setVisibility(View.VISIBLE);
1510                         userAgentTextView.setText(webViewDefaultUserAgentString);
1511
1512                         // Hide the custom user agent EditTex.
1513                         customUserAgentEditText.setVisibility(View.GONE);
1514                         break;
1515
1516                     case MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT:
1517                         // Hide the user agent TextView.
1518                         userAgentTextView.setVisibility(View.GONE);
1519
1520                         // Show the custom user agent EditText and set the current user agent name as the text.
1521                         customUserAgentEditText.setVisibility(View.VISIBLE);
1522                         customUserAgentEditText.setText(currentUserAgentName);
1523                         break;
1524
1525                     default:
1526                         // Show the user agent TextView and set the text from the user agent data array, which has one less entry than the spinner, so the position must be decremented.
1527                         userAgentTextView.setVisibility(View.VISIBLE);
1528                         userAgentTextView.setText(userAgentDataArray[position - 1]);
1529
1530                         // Hide `customUserAgentEditText`.
1531                         customUserAgentEditText.setVisibility(View.GONE);
1532                 }
1533             }
1534
1535             @Override
1536             public void onNothingSelected(AdapterView<?> parent) {
1537                 // Do nothing.
1538             }
1539         });
1540
1541         // Set the font size spinner listener.
1542         fontSizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1543             @Override
1544             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1545                 // Update the display options for `fontSizeTextView`.
1546                 if (position == 0) {  // System default font size has been selected.  Display `fontSizeTextView`.
1547                     fontSizeTextView.setVisibility(View.VISIBLE);
1548                 } else {  // A custom font size has been selected.  Hide `fontSizeTextView`.
1549                     fontSizeTextView.setVisibility(View.GONE);
1550                 }
1551             }
1552
1553             @Override
1554             public void onNothingSelected(AdapterView<?> parent) {
1555                 // Do nothing.
1556             }
1557         });
1558
1559         // Set the swipe to refresh spinner listener.
1560         swipeToRefreshSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1561             @Override
1562             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1563                 // Update the icon and the visibility of `nightModeTextView`.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
1564                 switch (position) {
1565                     case DomainsDatabaseHelper.SWIPE_TO_REFRESH_SYSTEM_DEFAULT:
1566                         if (defaultSwipeToRefresh) {  // Swipe to refresh enabled by default.
1567                             // Set the icon according to the theme.
1568                             if (darkTheme) {
1569                                 swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_dark));
1570                             } else {
1571                                 swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_light));
1572                             }
1573                         } else {  // Swipe to refresh disabled by default.
1574                             // Set the icon according to the theme.
1575                             if (darkTheme) {
1576                                 swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_dark));
1577                             } else {
1578                                 swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_light));
1579                             }
1580                         }
1581
1582                         // Show the swipe to refresh TextView.
1583                         swipeToRefreshTextView.setVisibility(View.VISIBLE);
1584                         break;
1585
1586                     case DomainsDatabaseHelper.SWIPE_TO_REFRESH_ENABLED:
1587                         // Set the icon according to the theme.
1588                         if (darkTheme) {
1589                             swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_dark));
1590                         } else {
1591                             swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_light));
1592                         }
1593
1594                         // Hide the swipe to refresh TextView.
1595                         swipeToRefreshTextView.setVisibility(View.GONE);
1596                         break;
1597
1598                     case DomainsDatabaseHelper.SWIPE_TO_REFRESH_DISABLED:
1599                         // Set the icon according to the theme.
1600                         if (darkTheme) {
1601                             swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_dark));
1602                         } else {
1603                             swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_light));
1604                         }
1605
1606                         // Hide the swipe to refresh TextView.
1607                         swipeToRefreshTextView.setVisibility(View.GONE);
1608                 }
1609             }
1610
1611             @Override
1612             public void onNothingSelected(AdapterView<?> parent) {
1613                 // Do nothing.
1614             }
1615         });
1616
1617         // Set the night mode spinner listener.
1618         nightModeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1619             @Override
1620             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1621                 // Update the icon and the visibility of `nightModeTextView`.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
1622                 switch (position) {
1623                     case DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT:
1624                         if (defaultNightMode) {  // Night mode enabled by default.
1625                             // Set the icon according to the theme.
1626                             if (darkTheme) {
1627                                 nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark));
1628                             } else {
1629                                 nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light));
1630                             }
1631                         } else {  // Night mode disabled by default.
1632                             // Set the icon according to the theme.
1633                             if (darkTheme) {
1634                                 nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark));
1635                             } else {
1636                                 nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light));
1637                             }
1638                         }
1639
1640                         // Show the night mode TextView.
1641                         nightModeTextView.setVisibility(View.VISIBLE);
1642                         break;
1643
1644                     case DomainsDatabaseHelper.NIGHT_MODE_ENABLED:
1645                         // Set the icon according to the theme.
1646                         if (darkTheme) {
1647                             nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark));
1648                         } else {
1649                             nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light));
1650                         }
1651
1652                         // Hide `nightModeTextView`.
1653                         nightModeTextView.setVisibility(View.GONE);
1654                         break;
1655
1656                     case DomainsDatabaseHelper.NIGHT_MODE_DISABLED:
1657                         // Set the icon according to the theme.
1658                         if (darkTheme) {
1659                             nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark));
1660                         } else {
1661                             nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light));
1662                         }
1663
1664                         // Hide `nightModeTextView`.
1665                         nightModeTextView.setVisibility(View.GONE);
1666                         break;
1667                 }
1668
1669                 // Create a `boolean` to store the current night mode setting.
1670                 boolean currentNightModeEnabled = (position == DomainsDatabaseHelper.NIGHT_MODE_ENABLED) || ((position == DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT) && defaultNightMode);
1671
1672                 // Disable the JavaScript `Switch` if night mode is enabled.
1673                 if (currentNightModeEnabled) {
1674                     javaScriptEnabledSwitch.setEnabled(false);
1675                 } else {
1676                     javaScriptEnabledSwitch.setEnabled(true);
1677                 }
1678
1679                 // Update the JavaScript icon.
1680                 if ((javaScriptEnabledInt == 1) || currentNightModeEnabled) {
1681                     javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled));
1682                 } else {
1683                     javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode));
1684                 }
1685
1686                 // Update the DOM storage status.
1687                 if ((javaScriptEnabledInt == 1) || currentNightModeEnabled) {  // JavaScript is enabled.
1688                     // Enable the DOM storage `Switch`.
1689                     domStorageEnabledSwitch.setEnabled(true);
1690
1691                     // Set the DOM storage status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
1692                     if (domStorageEnabledInt == 1) {  // Both JavaScript and DOM storage are enabled.
1693                         domStorageEnabledSwitch.setChecked(true);
1694                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
1695                     } else {  // JavaScript is enabled but DOM storage is disabled.
1696                         // Set the DOM storage switch to off.
1697                         domStorageEnabledSwitch.setChecked(false);
1698
1699                         // Set the icon according to the theme.
1700                         if (darkTheme) {
1701                             domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
1702                         } else {
1703                             domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
1704                         }
1705                     }
1706                 } else {  // JavaScript is disabled.
1707                     // Disable the DOM storage `Switch`.
1708                     domStorageEnabledSwitch.setEnabled(false);
1709
1710                     // Set the checked status of DOM storage.
1711                     if (domStorageEnabledInt == 1) {  // DOM storage is enabled but JavaScript is disabled.
1712                         domStorageEnabledSwitch.setChecked(true);
1713                     } else {  // Both JavaScript and DOM storage are disabled.
1714                         domStorageEnabledSwitch.setChecked(false);
1715                     }
1716
1717                     // Set the icon according to the theme.
1718                     if (darkTheme) {
1719                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_dark));
1720                     } else {
1721                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_light));
1722                     }
1723                 }
1724             }
1725
1726             @Override
1727             public void onNothingSelected(AdapterView<?> parent) {
1728                 // Do nothing.
1729             }
1730         });
1731
1732         // Set the display webpage images spinner listener.
1733         displayWebpageImagesSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1734             @Override
1735             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1736                 // Update the icon and the visibility of `displayImagesTextView`.
1737                 switch (position) {
1738                     case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_SYSTEM_DEFAULT:
1739                         if (defaultDisplayWebpageImages) {
1740                             // Set the icon according to the theme.
1741                             if (darkTheme) {
1742                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
1743                             } else {
1744                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
1745                             }
1746                         } else {
1747                             // Set the icon according to the theme.
1748                             if (darkTheme) {
1749                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
1750                             } else {
1751                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
1752                             }
1753                         }
1754
1755                         // Show `displayImagesTextView`.
1756                         displayImagesTextView.setVisibility(View.VISIBLE);
1757                         break;
1758
1759                     case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED:
1760                         // Set the icon according to the theme.
1761                         if (darkTheme) {
1762                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
1763                         } else {
1764                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
1765                         }
1766
1767                         // Hide `displayImagesTextView`.
1768                         displayImagesTextView.setVisibility(View.GONE);
1769                         break;
1770
1771                     case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED:
1772                         // Set the icon according to the theme.
1773                         if (darkTheme) {
1774                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
1775                         } else {
1776                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
1777                         }
1778
1779                         // Hide `displayImagesTextView`.
1780                         displayImagesTextView.setVisibility(View.GONE);
1781                         break;
1782                 }
1783             }
1784
1785             @Override
1786             public void onNothingSelected(AdapterView<?> parent) {
1787                 // Do nothing.
1788             }
1789         });
1790         
1791         // Set the pinned SSL certificate switch listener.
1792         pinnedSslCertificateSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1793             // Update the icon.
1794             if (isChecked) {  // SSL certificate pinning is enabled.
1795                 // Set the icon according to the theme.
1796                 if (darkTheme) {
1797                     pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark));
1798                 } else {
1799                     pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_light));
1800                 }
1801
1802                 // Update the visibility of the saved SSL certificate.
1803                 if (savedSslIssuedToCNameString == null) {
1804                     savedSslCardView.setVisibility(View.GONE);
1805                 } else {
1806                     savedSslCardView.setVisibility(View.VISIBLE);
1807                 }
1808
1809                 // Update the visibility of the current website SSL certificate.
1810                 if (DomainsActivity.sslIssuedToCName == null) {
1811                     // Hide the SSL certificate.
1812                     currentSslCardView.setVisibility(View.GONE);
1813
1814                     // Show the instruction.
1815                     noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
1816                 } else {
1817                     // Show the SSL certificate.
1818                     currentSslCardView.setVisibility(View.VISIBLE);
1819
1820                     // Hide the instruction.
1821                     noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1822                 }
1823
1824                 // Set the status of the radio buttons.
1825                 if (savedSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
1826                     // Check the saved SSL certificate radio button.
1827                     savedSslCertificateRadioButton.setChecked(true);
1828
1829                     // Uncheck the current website SSL certificate radio button.
1830                     currentWebsiteCertificateRadioButton.setChecked(false);
1831
1832                     // Set the background of the saved SSL certificate linear layout to be transparent.
1833                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1834
1835                     // Darken the background of the current website SSL certificate linear layout according to the theme.
1836                     if (darkTheme) {
1837                         currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1838                     } else {
1839                         currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1840                     }
1841
1842                     // Scroll to the current website SSL certificate card.
1843                     savedSslCardView.getParent().requestChildFocus(savedSslCardView, savedSslCardView);
1844                 } else if (currentSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1845                     // Check the current website SSL certificate radio button.
1846                     currentWebsiteCertificateRadioButton.setChecked(true);
1847
1848                     // Uncheck the saved SSL certificate radio button.
1849                     savedSslCertificateRadioButton.setChecked(false);
1850
1851                     // Set the background of the current website SSL certificate linear layout to be transparent.
1852                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1853
1854                     // Darken the background of the saved SSL certificate linear layout according to the theme.
1855                     if (darkTheme) {
1856                         savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1857                     } else {
1858                         savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1859                     }
1860
1861                     // Scroll to the current website SSL certificate card.
1862                     currentSslCardView.getParent().requestChildFocus(currentSslCardView, currentSslCardView);
1863                 } else {  // Neither SSL certificate is visible.
1864                     // Uncheck both radio buttons.
1865                     savedSslCertificateRadioButton.setChecked(false);
1866                     currentWebsiteCertificateRadioButton.setChecked(false);
1867
1868                     // Scroll to the current website SSL certificate card.
1869                     noCurrentWebsiteCertificateTextView.getParent().requestChildFocus(noCurrentWebsiteCertificateTextView, noCurrentWebsiteCertificateTextView);
1870                 }
1871             } else {  // SSL certificate pinning is disabled.
1872                 // Set the icon according to the theme.
1873                 if (darkTheme) {
1874                     pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark));
1875                 } else {
1876                     pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_light));
1877                 }
1878
1879                 // Hide the SSl certificates and instructions.
1880                 savedSslCardView.setVisibility(View.GONE);
1881                 currentSslCardView.setVisibility(View.GONE);
1882                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1883
1884                 // Uncheck the radio buttons.
1885                 savedSslCertificateRadioButton.setChecked(false);
1886                 currentWebsiteCertificateRadioButton.setChecked(false);
1887             }
1888         });
1889
1890         savedSslCardView.setOnClickListener((View view) -> {
1891             // Check the saved SSL certificate radio button.
1892             savedSslCertificateRadioButton.setChecked(true);
1893
1894             // Uncheck the current website SSL certificate radio button.
1895             currentWebsiteCertificateRadioButton.setChecked(false);
1896
1897             // Set the background of the saved SSL certificate linear layout to be transparent.
1898             savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1899
1900             // Darken the background of the current website SSL certificate linear layout according to the theme.
1901             if (darkTheme) {
1902                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1903             } else {
1904                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1905             }
1906         });
1907
1908         savedSslCertificateRadioButton.setOnClickListener((View view) -> {
1909             // Check the saved SSL certificate radio button.
1910             savedSslCertificateRadioButton.setChecked(true);
1911
1912             // Uncheck the current website SSL certificate radio button.
1913             currentWebsiteCertificateRadioButton.setChecked(false);
1914
1915             // Set the background of the saved SSL certificate linear layout to be transparent.
1916             savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1917
1918             // Darken the background of the current website SSL certificate linear layout according to the theme.
1919             if (darkTheme) {
1920                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1921             } else {
1922                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1923             }
1924         });
1925
1926         currentSslCardView.setOnClickListener((View view) -> {
1927             // Check the current website SSL certificate radio button.
1928             currentWebsiteCertificateRadioButton.setChecked(true);
1929
1930             // Uncheck the saved SSL certificate radio button.
1931             savedSslCertificateRadioButton.setChecked(false);
1932
1933             // Set the background of the current website SSL certificate linear layout to be transparent.
1934             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1935
1936             // Darken the background of the saved SSL certificate linear layout according to the theme.
1937             if (darkTheme) {
1938                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1939             } else {
1940                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1941             }
1942         });
1943
1944         currentWebsiteCertificateRadioButton.setOnClickListener((View view) -> {
1945             // Check the current website SSL certificate radio button.
1946             currentWebsiteCertificateRadioButton.setChecked(true);
1947
1948             // Uncheck the saved SSL certificate radio button.
1949             savedSslCertificateRadioButton.setChecked(false);
1950
1951             // Set the background of the current website SSL certificate linear layout to be transparent.
1952             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1953
1954             // Darken the background of the saved SSL certificate linear layout according to the theme.
1955             if (darkTheme) {
1956                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1957             } else {
1958                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1959             }
1960         });
1961
1962         // Set the pinned IP addresses switch listener.
1963         pinnedIpAddressesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1964             // Update the icon.
1965             if (isChecked) {  // IP addresses pinning is enabled.
1966                 // Set the icon according to the theme.
1967                 if (darkTheme) {
1968                     pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark));
1969                 } else {
1970                     pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_light));
1971                 }
1972
1973                 // Update the visibility of the saved IP addresses card view.
1974                 if (savedIpAddresses == null) {  // There are no saved IP addresses.
1975                     savedIpAddressesCardView.setVisibility(View.GONE);
1976                 } else {  // There are saved IP addresses.
1977                     savedIpAddressesCardView.setVisibility(View.VISIBLE);
1978                 }
1979
1980                 // Show the current IP addresses card view.
1981                 currentIpAddressesCardView.setVisibility(View.VISIBLE);
1982
1983                 // Set the status of the radio buttons.
1984                 if (savedIpAddressesCardView.getVisibility() == View.VISIBLE) {  // The saved IP addresses are visible.
1985                     // Check the saved IP addresses radio button.
1986                     savedIpAddressesRadioButton.setChecked(true);
1987
1988                     // Uncheck the current IP addresses radio button.
1989                     currentIpAddressesRadioButton.setChecked(false);
1990
1991                     // Set the background of the saved IP addresses linear layout to be transparent.
1992                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1993
1994                     // Darken the background of the current IP addresses linear layout according to the theme.
1995                     if (darkTheme) {
1996                         currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1997                     } else {
1998                         currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1999                     }
2000                 } else {  // The saved IP addresses are not visible.
2001                     // Check the current IP addresses radio button.
2002                     currentIpAddressesRadioButton.setChecked(true);
2003
2004                     // Uncheck the saved IP addresses radio button.
2005                     savedIpAddressesRadioButton.setChecked(false);
2006
2007                     // Set the background of the current IP addresses linear layout to be transparent.
2008                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2009
2010                     // Darken the background of the saved IP addresses linear layout according to the theme.
2011                     if (darkTheme) {
2012                         savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2013                     } else {
2014                         savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2015                     }
2016                 }
2017
2018                 // Scroll to the bottom of the card views.
2019                 currentIpAddressesCardView.getParent().requestChildFocus(currentIpAddressesCardView, currentIpAddressesCardView);
2020             } else {  // IP addresses pinning is disabled.
2021                 // Set the icon according to the theme.
2022                 if (darkTheme) {
2023                     pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark));
2024                 } else {
2025                     pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_light));
2026                 }
2027
2028                 // Hide the IP addresses card views.
2029                 savedIpAddressesCardView.setVisibility(View.GONE);
2030                 currentIpAddressesCardView.setVisibility(View.GONE);
2031
2032                 // Uncheck the radio buttons.
2033                 savedIpAddressesRadioButton.setChecked(false);
2034                 currentIpAddressesRadioButton.setChecked(false);
2035             }
2036         });
2037
2038         savedIpAddressesCardView.setOnClickListener((View view) -> {
2039             // Check the saved IP addresses radio button.
2040             savedIpAddressesRadioButton.setChecked(true);
2041
2042             // Uncheck the current website IP addresses radio button.
2043             currentIpAddressesRadioButton.setChecked(false);
2044
2045             // Set the background of the saved IP addresses linear layout to be transparent.
2046             savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2047
2048             // Darken the background of the current IP addresses linear layout according to the theme.
2049             if (darkTheme) {
2050                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2051             } else {
2052                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2053             }
2054         });
2055
2056         savedIpAddressesRadioButton.setOnClickListener((View view) -> {
2057             // Check the saved IP addresses radio button.
2058             savedIpAddressesRadioButton.setChecked(true);
2059
2060             // Uncheck the current website IP addresses radio button.
2061             currentIpAddressesRadioButton.setChecked(false);
2062
2063             // Set the background of the saved IP addresses linear layout to be transparent.
2064             savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2065
2066             // Darken the background of the current IP addresses linear layout according to the theme.
2067             if (darkTheme) {
2068                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2069             } else {
2070                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2071             }
2072         });
2073
2074         currentIpAddressesCardView.setOnClickListener((View view) -> {
2075             // Check the current IP addresses radio button.
2076             currentIpAddressesRadioButton.setChecked(true);
2077
2078             // Uncheck the saved IP addresses radio button.
2079             savedIpAddressesRadioButton.setChecked(false);
2080
2081             // Set the background of the current IP addresses linear layout to be transparent.
2082             currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2083
2084             // Darken the background of the saved IP addresses linear layout according to the theme.
2085             if (darkTheme) {
2086                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2087             } else {
2088                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2089             }
2090         });
2091
2092         currentIpAddressesRadioButton.setOnClickListener((View view) -> {
2093             // Check the current IP addresses radio button.
2094             currentIpAddressesRadioButton.setChecked(true);
2095
2096             // Uncheck the saved IP addresses radio button.
2097             savedIpAddressesRadioButton.setChecked(false);
2098
2099             // Set the background of the current IP addresses linear layout to be transparent.
2100             currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2101
2102             // Darken the background of the saved IP addresses linear layout according to the theme.
2103             if (darkTheme) {
2104                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2105             } else {
2106                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2107             }
2108         });
2109
2110         return domainSettingsView;
2111     }
2112
2113     private boolean checkDomainNameAgainstCertificate(String domainName, String certificateCommonName) {
2114         // Initialize `domainNamesMatch`.
2115         boolean domainNamesMatch = false;
2116
2117         // Check various wildcard permutations if `domainName` and `certificateCommonName` are not empty.
2118         // `noinspection ConstantCondition` removes Android Studio's incorrect lint warning that `domainName` can never be `null`.
2119         if ((domainName != null) && (certificateCommonName != null)) {
2120             // Check if the domains match.
2121             if (domainName.equals(certificateCommonName)) {
2122                 domainNamesMatch = true;
2123             }
2124
2125             // If `domainName` starts with a wildcard, check the base domain against all the subdomains of `certificateCommonName`.
2126             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2)) {
2127                 // Remove the initial `*.`.
2128                 String baseDomainName = domainName.substring(2);
2129
2130                 // Setup a copy of `certificateCommonName` to test subdomains.
2131                 String certificateCommonNameSubdomain = certificateCommonName;
2132
2133                 // Check all the subdomains in `certificateCommonNameSubdomain` against `baseDomainName`.
2134                 while (!domainNamesMatch && certificateCommonNameSubdomain.contains(".")) {  // Stop checking if we know that `domainNamesMatch` is `true` or if we run out of  `.`.
2135                     // Test the `certificateCommonNameSubdomain` against `baseDomainName`.
2136                     if (certificateCommonNameSubdomain.equals(baseDomainName)) {
2137                         domainNamesMatch = true;
2138                     }
2139
2140                     // Strip out the lowest subdomain of `certificateCommonNameSubdomain`.
2141                     try {
2142                         certificateCommonNameSubdomain = certificateCommonNameSubdomain.substring(certificateCommonNameSubdomain.indexOf(".") + 1);
2143                     } catch (IndexOutOfBoundsException e) {  // `certificateCommonNameSubdomain` ends with `.`.
2144                         certificateCommonNameSubdomain = "";
2145                     }
2146                 }
2147             }
2148
2149             // If `certificateCommonName` starts with a wildcard, check the base common name against all the subdomains of `domainName`.
2150             if (!domainNamesMatch && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
2151                 // Remove the initial `*.`.
2152                 String baseCertificateCommonName = certificateCommonName.substring(2);
2153
2154                 // Setup a copy of `domainName` to test subdomains.
2155                 String domainNameSubdomain = domainName;
2156
2157                 // Check all the subdomains in `domainNameSubdomain` against `baseCertificateCommonName`.
2158                 while (!domainNamesMatch && domainNameSubdomain.contains(".") && (domainNameSubdomain.length() > 2)) {
2159                     // Test the `domainNameSubdomain` against `baseCertificateCommonName`.
2160                     if (domainNameSubdomain.equals(baseCertificateCommonName)) {
2161                         domainNamesMatch = true;
2162                     }
2163
2164                     // Strip out the lowest subdomain of `domainNameSubdomain`.
2165                     try {
2166                         domainNameSubdomain = domainNameSubdomain.substring(domainNameSubdomain.indexOf(".") + 1);
2167                     } catch (IndexOutOfBoundsException e) { // `domainNameSubdomain` ends with `.`.
2168                         domainNameSubdomain = "";
2169                     }
2170                 }
2171             }
2172
2173             // If both names start with a wildcard, check if the root of one contains the root of the other.
2174             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2) && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
2175                 // Remove the wildcards.
2176                 String rootDomainName = domainName.substring(2);
2177                 String rootCertificateCommonName = certificateCommonName.substring(2);
2178
2179                 // Check if one name ends with the contents of the other.  If so, there will be overlap in the their wildcard subdomains.
2180                 if (rootDomainName.endsWith(rootCertificateCommonName) || rootCertificateCommonName.endsWith(rootDomainName)) {
2181                     domainNamesMatch = true;
2182                 }
2183             }
2184         }
2185
2186         return domainNamesMatch;
2187     }
2188 }