Redesign the user agents so they can autoupdate. https://redmine.stoutner.com/issues/216
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / fragments / DomainSettingsFragment.java
1 /*
2  * Copyright © 2017-2018 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.net.http.SslCertificate;
28 import android.os.Build;
29 import android.os.Bundle;
30 // We have to use `android.support.v4.app.Fragment` until minimum API >= 23.  Otherwise we cannot call `getContext()`.
31 import android.preference.PreferenceManager;
32 import android.support.annotation.NonNull;
33 import android.support.v4.app.Fragment;
34 import android.text.Editable;
35 import android.text.SpannableStringBuilder;
36 import android.text.Spanned;
37 import android.text.TextWatcher;
38 import android.text.style.ForegroundColorSpan;
39 import android.view.LayoutInflater;
40 import android.view.View;
41 import android.view.ViewGroup;
42 import android.webkit.WebView;
43 import android.widget.AdapterView;
44 import android.widget.ArrayAdapter;
45 import android.widget.CompoundButton;
46 import android.widget.EditText;
47 import android.widget.ImageView;
48 import android.widget.LinearLayout;
49 import android.widget.RadioButton;
50 import android.widget.Spinner;
51 import android.widget.Switch;
52 import android.widget.TextView;
53
54 import com.stoutner.privacybrowser.R;
55 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
56 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
57
58 import java.text.DateFormat;
59 import java.util.Calendar;
60 import java.util.Date;
61
62 public class DomainSettingsFragment extends Fragment {
63     // `DATABASE_ID` is used by activities calling this fragment.
64     public static final String DATABASE_ID = "database_id";
65
66     // `databaseId` is public static so it can be accessed from `DomainsActivity`. It is also used in `onCreate()` and `onCreateView()`.
67     public static int databaseId;
68
69     @Override
70     public void onCreate(Bundle savedInstanceState) {
71         super.onCreate(savedInstanceState);
72
73         // Remove the lint warning that `getArguments` might be null.
74         assert getArguments() != null;
75
76         // Store the database id in `databaseId`.
77         databaseId = getArguments().getInt(DATABASE_ID);
78     }
79
80     // The deprecated `getDrawable()` must be used until the minimum API >= 21.
81     @SuppressWarnings("deprecation")
82     @Override
83     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
84         // Inflate `domain_settings_fragment`.  `false` does not attach it to the root `container`.
85         View domainSettingsView = inflater.inflate(R.layout.domain_settings_fragment, container, false);
86
87         // Get a handle for the `Context` and the `Resources`.
88         Context context = getContext();
89         final Resources resources = getResources();
90
91         // Get a handle for the shared preference.
92         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
93
94         // Store the default settings.
95         final String defaultUserAgentName = sharedPreferences.getString("user_agent", "Privacy Browser");
96         final String defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0");
97         String defaultFontSizeString = sharedPreferences.getString("default_font_size", "100");
98         final boolean defaultDisplayWebpageImagesBoolean = sharedPreferences.getBoolean("display_website_images", true);
99         final boolean defaultNightModeBoolean = sharedPreferences.getBoolean("night_mode", false);
100
101         // Get handles for the views in the fragment.
102         final EditText domainNameEditText = domainSettingsView.findViewById(R.id.domain_settings_name_edittext);
103         final Switch javaScriptEnabledSwitch = domainSettingsView.findViewById(R.id.domain_settings_javascript_switch);
104         final ImageView javaScriptImageView = domainSettingsView.findViewById(R.id.domain_settings_javascript_imageview);
105         Switch firstPartyCookiesEnabledSwitch = domainSettingsView.findViewById(R.id.domain_settings_first_party_cookies_switch);
106         final ImageView firstPartyCookiesImageView = domainSettingsView.findViewById(R.id.domain_settings_first_party_cookies_imageview);
107         LinearLayout thirdPartyCookiesLinearLayout = domainSettingsView.findViewById(R.id.domain_settings_third_party_cookies_linearlayout);
108         final Switch thirdPartyCookiesEnabledSwitch = domainSettingsView.findViewById(R.id.domain_settings_third_party_cookies_switch);
109         final ImageView thirdPartyCookiesImageView = domainSettingsView.findViewById(R.id.domain_settings_third_party_cookies_imageview);
110         final Switch domStorageEnabledSwitch = domainSettingsView.findViewById(R.id.domain_settings_dom_storage_switch);
111         final ImageView domStorageImageView = domainSettingsView.findViewById(R.id.domain_settings_dom_storage_imageview);
112         Switch formDataEnabledSwitch = domainSettingsView.findViewById(R.id.domain_settings_form_data_switch);
113         final ImageView formDataImageView = domainSettingsView.findViewById(R.id.domain_settings_form_data_imageview);
114         Switch easyListSwitch = domainSettingsView.findViewById(R.id.domain_settings_easylist_switch);
115         ImageView easyListImageView = domainSettingsView.findViewById(R.id.domain_settings_easylist_imageview);
116         Switch easyPrivacySwitch = domainSettingsView.findViewById(R.id.domain_settings_easyprivacy_switch);
117         ImageView easyPrivacyImageView = domainSettingsView.findViewById(R.id.domain_settings_easyprivacy_imageview);
118         Switch fanboysAnnoyanceListSwitch = domainSettingsView.findViewById(R.id.domain_settings_fanboys_annoyance_list_switch);
119         ImageView fanboysAnnoyanceListImageView = domainSettingsView.findViewById(R.id.domain_settings_fanboys_annoyance_list_imageview);
120         Switch fanboysSocialBlockingListSwitch = domainSettingsView.findViewById(R.id.domain_settings_fanboys_social_blocking_list_switch);
121         ImageView fanboysSocialBlockingListImageView = domainSettingsView.findViewById(R.id.domain_settings_fanboys_social_blocking_list_imageview);
122         final Spinner userAgentSpinner = domainSettingsView.findViewById(R.id.domain_settings_user_agent_spinner);
123         final TextView userAgentTextView = domainSettingsView.findViewById(R.id.domain_settings_user_agent_textview);
124         final EditText customUserAgentEditText = domainSettingsView.findViewById(R.id.domain_settings_custom_user_agent_edittext);
125         final Spinner fontSizeSpinner = domainSettingsView.findViewById(R.id.domain_settings_font_size_spinner);
126         final TextView fontSizeTextView = domainSettingsView.findViewById(R.id.domain_settings_font_size_textview);
127         final ImageView displayWebpageImagesImageView = domainSettingsView.findViewById(R.id.domain_settings_display_webpage_images_imageview);
128         final Spinner displayWebpageImagesSpinner = domainSettingsView.findViewById(R.id.domain_settings_display_webpage_images_spinner);
129         final TextView displayImagesTextView = domainSettingsView.findViewById(R.id.domain_settings_display_webpage_images_textview);
130         final ImageView nightModeImageView = domainSettingsView.findViewById(R.id.domain_settings_night_mode_imageview);
131         final Spinner nightModeSpinner = domainSettingsView.findViewById(R.id.domain_settings_night_mode_spinner);
132         final TextView nightModeTextView = domainSettingsView.findViewById(R.id.domain_settings_night_mode_textview);
133         final ImageView pinnedSslCertificateImageView = domainSettingsView.findViewById(R.id.domain_settings_pinned_ssl_certificate_imageview);
134         Switch pinnedSslCertificateSwitch = domainSettingsView.findViewById(R.id.domain_settings_pinned_ssl_certificate_switch);
135         final LinearLayout savedSslCertificateLinearLayout = domainSettingsView.findViewById(R.id.saved_ssl_certificate_linearlayout);
136         final RadioButton savedSslCertificateRadioButton = domainSettingsView.findViewById(R.id.saved_ssl_certificate_radiobutton);
137         final TextView savedSslCertificateIssuedToCNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_cname);
138         TextView savedSslCertificateIssuedToONameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_oname);
139         TextView savedSslCertificateIssuedToUNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_uname);
140         TextView savedSslCertificateIssuedByCNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_cname);
141         TextView savedSslCertificateIssuedByONameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_oname);
142         TextView savedSslCertificateIssuedByUNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_uname);
143         TextView savedSslCertificateStartDateTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_start_date);
144         TextView savedSslCertificateEndDateTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_end_date);
145         final LinearLayout currentWebsiteCertificateLinearLayout = domainSettingsView.findViewById(R.id.current_website_certificate_linearlayout);
146         final RadioButton currentWebsiteCertificateRadioButton = domainSettingsView.findViewById(R.id.current_website_certificate_radiobutton);
147         final TextView currentWebsiteCertificateIssuedToCNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_cname);
148         TextView currentWebsiteCertificateIssuedToONameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_oname);
149         TextView currentWebsiteCertificateIssuedToUNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_uname);
150         TextView currentWebsiteCertificateIssuedByCNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_cname);
151         TextView currentWebsiteCertificateIssuedByONameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_oname);
152         TextView currentWebsiteCertificateIssuedByUNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_uname);
153         TextView currentWebsiteCertificateStartDateTextView = domainSettingsView.findViewById(R.id.current_website_certificate_start_date);
154         TextView currentWebsiteCertificateEndDateTextView = domainSettingsView.findViewById(R.id.current_website_certificate_end_date);
155         final TextView noCurrentWebsiteCertificateTextView = domainSettingsView.findViewById(R.id.no_current_website_certificate);
156
157         // Setup the SSL certificate labels.
158         final String cNameLabel = getString(R.string.common_name) + "  ";
159         String oNameLabel = getString(R.string.organization) + "  ";
160         String uNameLabel = getString(R.string.organizational_unit) + "  ";
161         String startDateLabel = getString(R.string.start_date) + "  ";
162         String endDateLabel = getString(R.string.end_date) + "  ";
163
164         // Get the current website SSL certificate
165         final SslCertificate currentWebsiteSslCertificate = MainWebViewActivity.sslCertificate;
166
167         // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
168         DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0);
169
170         // Get the database `Cursor` for this ID and move it to the first row.
171         Cursor domainCursor = domainsDatabaseHelper.getCursorForId(databaseId);
172         domainCursor.moveToFirst();
173
174         // Save the `Cursor` entries as variables.
175         String domainNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME));
176         final int javaScriptEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT));
177         int firstPartyCookiesEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES));
178         int thirdPartyCookiesEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES));
179         final int domStorageEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE));
180         int formDataEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA));
181         int easyListEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST));
182         int easyPrivacyEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY));
183         int fanboysAnnoyanceListInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST));
184         int fanboysSocialBlockingListInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST));
185         final String currentUserAgentName = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
186         int fontSizeInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
187         int displayImagesInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
188         int nightModeInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE));
189         int pinnedSslCertificateInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE));
190         final String savedSslCertificateIssuedToCNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME));
191         String savedSslCertificateIssuedToONameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION));
192         String savedSslCertificateIssuedToUNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT));
193         String savedSslCertificateIssuedByCNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME));
194         String savedSslCertificateIssuedByONameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION));
195         String savedSslCertificateIssuedByUNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT));
196
197         // Initialize the saved SSL certificate date variables.
198         Date savedSslCertificateStartDate = null;
199         Date savedSslCertificateEndDate = null;
200
201         // Only get the saved SSL certificate dates from the cursor if they are not set to `0`.
202         if (domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) != 0) {
203             savedSslCertificateStartDate = new Date(domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)));
204         }
205
206         if (domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) != 0) {
207             savedSslCertificateEndDate = new Date(domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)));
208         }
209
210         // Create `ArrayAdapters` for the `Spinners`and their `entry values`.
211         ArrayAdapter<CharSequence> translatedUserAgentArrayAdapter = ArrayAdapter.createFromResource(context, R.array.translated_domain_settings_user_agent_names, R.layout.domain_settings_spinner_item);
212         ArrayAdapter<CharSequence> fontSizeArrayAdapter = ArrayAdapter.createFromResource(context, R.array.domain_settings_font_size_entries, R.layout.domain_settings_spinner_item);
213         ArrayAdapter<CharSequence> fontSizeEntryValuesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.domain_settings_font_size_entry_values, R.layout.domain_settings_spinner_item);
214         final ArrayAdapter<CharSequence> displayImagesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.display_webpage_images_array, R.layout.domain_settings_spinner_item);
215         ArrayAdapter<CharSequence> nightModeArrayAdapter = ArrayAdapter.createFromResource(context, R.array.night_mode_array, R.layout.domain_settings_spinner_item);
216
217         // Set the `DropDownViewResource` on the `Spinners`.
218         translatedUserAgentArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_item);
219         fontSizeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_item);
220         displayImagesArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_item);
221         nightModeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_item);
222
223         // Set the `ArrayAdapters` for the `Spinners`.
224         userAgentSpinner.setAdapter(translatedUserAgentArrayAdapter);
225         fontSizeSpinner.setAdapter(fontSizeArrayAdapter);
226         displayWebpageImagesSpinner.setAdapter(displayImagesArrayAdapter);
227         nightModeSpinner.setAdapter(nightModeArrayAdapter);
228
229         // Create a `SpannableStringBuilder` for each `TextView` that needs multiple colors of text.
230         SpannableStringBuilder savedSslCertificateIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslCertificateIssuedToCNameString);
231         SpannableStringBuilder savedSslCertificateIssuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + savedSslCertificateIssuedToONameString);
232         SpannableStringBuilder savedSslCertificateIssuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + savedSslCertificateIssuedToUNameString);
233         SpannableStringBuilder savedSslCertificateIssuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslCertificateIssuedByCNameString);
234         SpannableStringBuilder savedSslCertificateIssuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + savedSslCertificateIssuedByONameString);
235         SpannableStringBuilder savedSslCertificateIssuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + savedSslCertificateIssuedByUNameString);
236
237         // Initialize the `SpannableStringBuilders` for the SSL certificate dates.
238         SpannableStringBuilder savedSslCertificateStartDateStringBuilder;
239         SpannableStringBuilder savedSslCertificateEndDateStringBuilder;
240
241         // Leave the SSL certificate dates empty if they are `null`.
242         if (savedSslCertificateStartDate == null) {
243             savedSslCertificateStartDateStringBuilder = new SpannableStringBuilder(startDateLabel);
244         } else {
245             savedSslCertificateStartDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslCertificateStartDate));
246         }
247
248         if (savedSslCertificateEndDate == null) {
249             savedSslCertificateEndDateStringBuilder = new SpannableStringBuilder(endDateLabel);
250         } else {
251             savedSslCertificateEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslCertificateEndDate));
252         }
253
254         // Create a red `ForegroundColorSpan`.  We have to use the deprecated `getColor` until API >= 23.
255         final ForegroundColorSpan redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
256
257         // Create a blue `ForegroundColorSpan`.
258         final ForegroundColorSpan blueColorSpan;
259
260         // Set `blueColorSpan` according to the theme.  We have to use the deprecated `getColor()` until API >= 23.
261         if (MainWebViewActivity.darkTheme) {
262             //noinspection deprecation
263             blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_400));
264         } else {
265             //noinspection deprecation
266             blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
267         }
268
269         // Set the domain name from the the database cursor.
270         domainNameEditText.setText(domainNameString);
271
272         // Update the certificates' `Common Name` color when the domain name text changes.
273         domainNameEditText.addTextChangedListener(new TextWatcher() {
274             @Override
275             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
276                 // Do nothing.
277             }
278
279             @Override
280             public void onTextChanged(CharSequence s, int start, int before, int count) {
281                 // Do nothing.
282             }
283
284             @Override
285             public void afterTextChanged(Editable s) {
286                 // Get the new domain name.
287                 String newDomainName = domainNameEditText.getText().toString();
288
289                 // Check the saved SSL certificate against the new domain name.
290                 boolean savedSslCertificateMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, savedSslCertificateIssuedToCNameString);
291
292                 // Create a `SpannableStringBuilder` for the saved certificate `Common Name`.
293                 SpannableStringBuilder savedSslCertificateCommonNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslCertificateIssuedToCNameString);
294
295                 // Format the saved certificate `Common Name` color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
296                 if (savedSslCertificateMatchesNewDomainName) {
297                     savedSslCertificateCommonNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslCertificateCommonNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
298                 } else {
299                     savedSslCertificateCommonNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), savedSslCertificateCommonNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
300                 }
301
302                 // Update `savedSslCertificateIssuedToCNameTextView`.
303                 savedSslCertificateIssuedToCNameTextView.setText(savedSslCertificateCommonNameStringBuilder);
304
305                 // Update the current website certificate if it exists.
306                 if (currentWebsiteSslCertificate != null) {
307                     // Get the current website certificate `Common Name`.
308                     String currentWebsiteCertificateCommonName = currentWebsiteSslCertificate.getIssuedTo().getCName();
309
310                     // Check the current website certificate against the new domain name.
311                     boolean currentWebsiteCertificateMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, currentWebsiteCertificateCommonName);
312
313                     // Create a `SpannableStringBuilder` for the current website certificate `Common Name`.
314                     SpannableStringBuilder currentWebsiteCertificateCommonNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentWebsiteCertificateCommonName);
315
316                     // Format the current certificate `Common Name` color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
317                     if (currentWebsiteCertificateMatchesNewDomainName) {
318                         currentWebsiteCertificateCommonNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentWebsiteCertificateCommonNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
319                     } else {
320                         currentWebsiteCertificateCommonNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), currentWebsiteCertificateCommonNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
321                     }
322
323                     // Update `currentWebsiteCertificateIssuedToCNameTextView`.
324                     currentWebsiteCertificateIssuedToCNameTextView.setText(currentWebsiteCertificateCommonNameStringBuilder);
325                 }
326             }
327         });
328
329         // Create a `boolean` to track if night mode is enabled.
330         boolean nightModeEnabled = (nightModeInt == DomainsDatabaseHelper.NIGHT_MODE_ENABLED) || ((nightModeInt == DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT) && defaultNightModeBoolean);
331
332         // Disable the JavaScript `Switch` if night mode is enabled.
333         if (nightModeEnabled) {
334             javaScriptEnabledSwitch.setEnabled(false);
335         } else {
336             javaScriptEnabledSwitch.setEnabled(true);
337         }
338
339         // Set the JavaScript icon.
340         if ((javaScriptEnabledInt == 1) || nightModeEnabled) {
341             javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled));
342         } else {
343             javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode));
344         }
345
346         // Set the JavaScript `Switch` status.
347         if (javaScriptEnabledInt == 1) {  // JavaScript is enabled.
348             javaScriptEnabledSwitch.setChecked(true);
349         } else {  // JavaScript is disabled.
350             javaScriptEnabledSwitch.setChecked(false);
351         }
352
353         // 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.
354         if (firstPartyCookiesEnabledInt == 1) {  // First-party cookies are enabled.
355             firstPartyCookiesEnabledSwitch.setChecked(true);
356             firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_enabled));
357         } else {  // First-party cookies are disabled.
358             firstPartyCookiesEnabledSwitch.setChecked(false);
359
360             // Set the icon according to the theme.
361             if (MainWebViewActivity.darkTheme) {
362                 firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
363             } else {
364                 firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
365             }
366         }
367
368         // Only display third-party cookies if SDK_INT >= 21.
369         if (Build.VERSION.SDK_INT >= 21) {  // Third-party cookies can be configured for API >= 21.
370             // Only enable third-party-cookies if first-party cookies are enabled.
371             if (firstPartyCookiesEnabledInt == 1) {  // First-party cookies are enabled.
372                 // 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.
373                 if (thirdPartyCookiesEnabledInt == 1) {  // Both first-party and third-party cookies are enabled.
374                     thirdPartyCookiesEnabledSwitch.setChecked(true);
375                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
376                 } else {  // First party cookies are enabled but third-party cookies are disabled.
377                     thirdPartyCookiesEnabledSwitch.setChecked(false);
378
379                     // Set the icon according to the theme.
380                     if (MainWebViewActivity.darkTheme) {
381                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
382                     } else {
383                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
384                     }
385                 }
386             } else {  // First-party cookies are disabled.
387                 // Set the status of third-party cookies.
388                 if (thirdPartyCookiesEnabledInt == 1) {
389                     thirdPartyCookiesEnabledSwitch.setChecked(true);
390                 } else {
391                     thirdPartyCookiesEnabledSwitch.setChecked(false);
392                 }
393
394                 // Disable the third-party cookies switch.
395                 thirdPartyCookiesEnabledSwitch.setEnabled(false);
396
397                 // Set the icon according to the theme.
398                 if (MainWebViewActivity.darkTheme) {
399                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_dark));
400                 } else {
401                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_light));
402                 }
403             }
404         } else {  // Third-party cookies cannot be configured for API <= 21.
405             // Hide the `LinearLayout` for third-party cookies.
406             thirdPartyCookiesLinearLayout.setVisibility(View.GONE);
407         }
408
409         // Only enable DOM storage if JavaScript is enabled.
410         if ((javaScriptEnabledInt == 1) || nightModeEnabled) {  // JavaScript is enabled.
411             // Enable the DOM storage `Switch`.
412             domStorageEnabledSwitch.setEnabled(true);
413
414             // Set the DOM storage status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
415             if (domStorageEnabledInt == 1) {  // Both JavaScript and DOM storage are enabled.
416                 domStorageEnabledSwitch.setChecked(true);
417                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
418             } else {  // JavaScript is enabled but DOM storage is disabled.
419                 // Set the DOM storage switch to off.
420                 domStorageEnabledSwitch.setChecked(false);
421
422                 // Set the icon according to the theme.
423                 if (MainWebViewActivity.darkTheme) {
424                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
425                 } else {
426                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
427                 }
428             }
429         } else {  // JavaScript is disabled.
430             // Disable the DOM storage `Switch`.
431             domStorageEnabledSwitch.setEnabled(false);
432
433             // Set the checked status of DOM storage.
434             if (domStorageEnabledInt == 1) {  // DOM storage is enabled but JavaScript is disabled.
435                 domStorageEnabledSwitch.setChecked(true);
436             } else {  // Both JavaScript and DOM storage are disabled.
437                 domStorageEnabledSwitch.setChecked(false);
438             }
439
440             // Set the icon according to the theme.
441             if (MainWebViewActivity.darkTheme) {
442                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_dark));
443             } else {
444                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_light));
445             }
446         }
447
448         // Set the form data status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
449         if (formDataEnabledInt == 1) {  // Form data is on.
450             formDataEnabledSwitch.setChecked(true);
451             formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_enabled));
452         } else {  // Form data is off.
453             // Turn the form data switch to off.
454             formDataEnabledSwitch.setChecked(false);
455
456             // Set the icon according to the theme.
457             if (MainWebViewActivity.darkTheme) {
458                 formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_dark));
459             } else {
460                 formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_light));
461             }
462         }
463
464         // Set the EasyList status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
465         if (easyListEnabledInt == 1) {  // EasyList is on.
466             // Turn the switch on.
467             easyListSwitch.setChecked(true);
468
469             // Set the icon according to the theme.
470             if (MainWebViewActivity.darkTheme) {
471                 easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_dark));
472             } else {
473                 easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_light));
474             }
475         } else {  // EasyList is off.
476             // Turn the switch off.
477             easyListSwitch.setChecked(false);
478
479             // Set the icon according to the theme.
480             if (MainWebViewActivity.darkTheme) {
481                 easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_dark));
482             } else {
483                 easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_light));
484             }
485         }
486
487         // Set the EasyPrivacy status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
488         if (easyPrivacyEnabledInt == 1) {  // EasyPrivacy is on.
489             // Turn the switch on.
490             easyPrivacySwitch.setChecked(true);
491
492             // Set the icon according to the theme.
493             if (MainWebViewActivity.darkTheme) {
494                 easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_dark));
495             } else {
496                 easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_light));
497             }
498         } else {  // EasyPrivacy is off.
499             // Turn the switch off.
500             easyPrivacySwitch.setChecked(false);
501
502             // Set the icon according to the theme.
503             if (MainWebViewActivity.darkTheme) {
504                 easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_dark));
505             } else {
506                 easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_light));
507             }
508         }
509
510         // 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.
511         if (fanboysAnnoyanceListInt == 1) {  // Fanboy's Annoyance List is on.
512             // Turn the switch on.
513             fanboysAnnoyanceListSwitch.setChecked(true);
514
515             // Set the icon according to the theme.
516             if (MainWebViewActivity.darkTheme) {
517                 fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
518             } else {
519                 fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
520             }
521         } else {  // Fanboy's Annoyance List is off.
522             // Turn the switch off.
523             fanboysAnnoyanceListSwitch.setChecked(false);
524
525             // Set the icon according to the theme.
526             if (MainWebViewActivity.darkTheme) {
527                 fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
528             } else {
529                 fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
530             }
531         }
532
533         // Only enable Fanboy's Social Blocking List if Fanboy's Annoyance List is off.
534         if (fanboysAnnoyanceListInt == 0) {  // Fanboy's Annoyance List is on.
535             // 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.
536             if (fanboysSocialBlockingListInt == 1) {  // Fanboy's Social Blocking List is on.
537                 // Enable the switch and turn it on.
538                 fanboysSocialBlockingListSwitch.setEnabled(true);
539                 fanboysSocialBlockingListSwitch.setChecked(true);
540
541                 // Set the icon according to the theme.
542                 if (MainWebViewActivity.darkTheme) {
543                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
544                 } else {
545                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
546                 }
547             } else {  // Fanboy's Social Blocking List is off.
548                 // Enable the switch but turn it off.
549                 fanboysSocialBlockingListSwitch.setEnabled(true);
550                 fanboysSocialBlockingListSwitch.setChecked(false);
551
552                 // Set the icon according to the theme.
553                 if (MainWebViewActivity.darkTheme) {
554                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
555                 } else {
556                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
557                 }
558             }
559         } else {  // Fanboy's Annoyance List is on.
560             // 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.
561             if (fanboysSocialBlockingListInt == 1) {  // Fanboy's Social Blocking List is on.
562                 // Disable the switch but turn it on.
563                 fanboysSocialBlockingListSwitch.setEnabled(false);
564                 fanboysSocialBlockingListSwitch.setChecked(true);
565             } else {  // Fanboy's Social Blocking List is off.
566                 // Disable the switch and turn it off.
567                 fanboysSocialBlockingListSwitch.setEnabled(false);
568                 fanboysSocialBlockingListSwitch.setChecked(false);
569             }
570
571             // Set the icon according to the theme.
572             if (MainWebViewActivity.darkTheme) {
573                 fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_dark));
574             } else {
575                 fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_light));
576             }
577         }
578
579         // Inflated a WebView to get the default user agent.
580         // `@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.
581         @SuppressLint("InflateParams") View bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false);
582         WebView bareWebView = bareWebViewLayout.findViewById(R.id.bare_webview);
583         final String webViewDefaultUserAgentString = bareWebView.getSettings().getUserAgentString();
584
585         // Get a handle for the user agent array adapter.  This array does not contain the `System default` entry.
586         ArrayAdapter<CharSequence> userAgentNamesArray = ArrayAdapter.createFromResource(context, R.array.user_agent_names, R.layout.domain_settings_spinner_item);
587
588         // Get the positions of the user agent and the default user agent.
589         int userAgentArrayPosition = userAgentNamesArray.getPosition(currentUserAgentName);
590         int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
591
592         // Get a handle for the user agent data array.  This array does not contain the `System default` entry.
593         String[] userAgentDataArray = resources.getStringArray(R.array.user_agent_data);
594
595         // Set the user agent text.
596         if (currentUserAgentName.equals(getString(R.string.system_default_user_agent))) {  // Use the system default user agent.
597             // Set the user agent according to the system default.
598             switch (defaultUserAgentArrayPosition) {
599                 case MainWebViewActivity.UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
600                     // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
601                     userAgentTextView.setText(defaultUserAgentName);
602                     break;
603
604                 case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
605                     // Display the `WebView` default user agent.
606                     userAgentTextView.setText(webViewDefaultUserAgentString);
607                     break;
608
609                 case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
610                     // Display the custom user agent.
611                     userAgentTextView.setText(defaultCustomUserAgentString);
612                     break;
613
614                 default:
615                     // Get the user agent string from the user agent data array.
616                     userAgentTextView.setText(userAgentDataArray[defaultUserAgentArrayPosition]);
617             }
618         } else if (userAgentArrayPosition == MainWebViewActivity.UNRECOGNIZED_USER_AGENT) {  // A custom user agent is stored in the current user agent name.
619             // Set the user agent spinner to `Custom user agent`.
620             userAgentSpinner.setSelection(MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT);
621
622             // Hide the user agent TextView.
623             userAgentTextView.setVisibility(View.GONE);
624
625             // Show the custom user agent EditText and set the current user agent name as the text.
626             customUserAgentEditText.setVisibility(View.VISIBLE);
627             customUserAgentEditText.setText(currentUserAgentName);
628         } else {  // The user agent name contains one of the canonical user agents.
629             // 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.
630             userAgentSpinner.setSelection(userAgentArrayPosition + 1);
631
632             // Show the user agent TextView.
633             userAgentTextView.setVisibility(View.VISIBLE);
634
635             // Hide the custom user agent EditText.
636             customUserAgentEditText.setVisibility(View.GONE);
637
638             // Set the user agent text.
639             switch (userAgentArrayPosition) {
640                 case MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT:
641                     // Display the WebView default user agent.
642                     userAgentTextView.setText(webViewDefaultUserAgentString);
643                     break;
644
645                 default:
646                     // 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.
647                     userAgentTextView.setText(userAgentDataArray[userAgentArrayPosition + 1]);
648             }
649         }
650
651         // Open the user agent spinner when the `TextView` is clicked.
652         userAgentTextView.setOnClickListener((View v) -> {
653             // Open the user agent spinner.
654             userAgentSpinner.performClick();
655         });
656
657         // Set the selected font size.
658         int fontSizeArrayPosition = fontSizeEntryValuesArrayAdapter.getPosition(String.valueOf(fontSizeInt));
659         fontSizeSpinner.setSelection(fontSizeArrayPosition);
660
661         // Set the default font size text.
662         int defaultFontSizeArrayPosition = fontSizeEntryValuesArrayAdapter.getPosition(defaultFontSizeString);
663         fontSizeTextView.setText(fontSizeArrayAdapter.getItem(defaultFontSizeArrayPosition));
664
665         // Set the display options for `fontSizeTextView`.
666         if (fontSizeArrayPosition == 0) {  // System default font size is selected.  Display `fontSizeTextView`.
667             fontSizeTextView.setVisibility(View.VISIBLE);
668         } else {  // A custom font size is specified.  Hide `fontSizeTextView`.
669             fontSizeTextView.setVisibility(View.GONE);
670         }
671
672         // Open the font size spinner when the `TextView` is clicked.
673         fontSizeTextView.setOnClickListener((View v) -> {
674             // Open the user agent spinner.
675             fontSizeSpinner.performClick();
676         });
677
678         // Display the website images mode in the spinner.
679         displayWebpageImagesSpinner.setSelection(displayImagesInt);
680
681         // Set the default display images text.
682         if (defaultDisplayWebpageImagesBoolean) {
683             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED));
684         } else {
685             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED));
686         }
687
688         // 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.
689         switch (displayImagesInt) {
690             case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_SYSTEM_DEFAULT:
691                 if (defaultDisplayWebpageImagesBoolean) {  // Display webpage images enabled by default.
692                     // Set the icon according to the theme.
693                     if (MainWebViewActivity.darkTheme) {
694                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
695                     } else {
696                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
697                     }
698                 } else {  // Display webpage images disabled by default.
699                     // Set the icon according to the theme.
700                     if (MainWebViewActivity.darkTheme) {
701                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
702                     } else {
703                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
704                     }
705                 }
706
707                 // Show `displayImagesTextView`.
708                 displayImagesTextView.setVisibility(View.VISIBLE);
709                 break;
710
711             case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED:
712                 // Set the icon according to the theme.
713                 if (MainWebViewActivity.darkTheme) {
714                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
715                 } else {
716                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
717                 }
718
719                 // Hide `displayImagesTextView`.
720                 displayImagesTextView.setVisibility(View.GONE);
721                 break;
722
723             case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED:
724                 // Set the icon according to the theme.
725                 if (MainWebViewActivity.darkTheme) {
726                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
727                 } else {
728                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
729                 }
730
731                 // Hide `displayImagesTextView`.
732                 displayImagesTextView.setVisibility(View.GONE);
733                 break;
734         }
735
736         // Open the display images spinner when the `TextView` is clicked.
737         displayImagesTextView.setOnClickListener((View v) -> {
738             // Open the user agent spinner.
739             displayWebpageImagesSpinner.performClick();
740         });
741
742         // Display the night mode in the spinner.
743         nightModeSpinner.setSelection(nightModeInt);
744
745         // Set the default night mode text.
746         if (defaultNightModeBoolean) {
747             nightModeTextView.setText(nightModeArrayAdapter.getItem(DomainsDatabaseHelper.NIGHT_MODE_ENABLED));
748         } else {
749             nightModeTextView.setText(nightModeArrayAdapter.getItem(DomainsDatabaseHelper.NIGHT_MODE_DISABLED));
750         }
751
752         // 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.
753         switch (displayImagesInt) {
754             case DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT:
755                 if (defaultNightModeBoolean) {  // Night mode enabled by default.
756                     // Set the icon according to the theme.
757                     if (MainWebViewActivity.darkTheme) {
758                         nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark));
759                     } else {
760                         nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light));
761                     }
762                 } else {  // Night mode disabled by default.
763                     // Set the icon according to the theme.
764                     if (MainWebViewActivity.darkTheme) {
765                         nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark));
766                     } else {
767                         nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light));
768                     }
769                 }
770
771                 // Show `nightModeTextView`.
772                 nightModeTextView.setVisibility(View.VISIBLE);
773                 break;
774
775             case DomainsDatabaseHelper.NIGHT_MODE_ENABLED:
776                 // Set the icon according to the theme.
777                 if (MainWebViewActivity.darkTheme) {
778                     nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark));
779                 } else {
780                     nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light));
781                 }
782
783                 // Hide `nightModeTextView`.
784                 nightModeTextView.setVisibility(View.GONE);
785                 break;
786
787             case DomainsDatabaseHelper.NIGHT_MODE_DISABLED:
788                 // Set the icon according to the theme.
789                 if (MainWebViewActivity.darkTheme) {
790                     nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark));
791                 } else {
792                     nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light));
793                 }
794
795                 // Hide `nightModeTextView`.
796                 nightModeTextView.setVisibility(View.GONE);
797                 break;
798         }
799
800         // Open the night mode spinner when the `TextView` is clicked.
801         nightModeTextView.setOnClickListener((View v) -> {
802             // Open the user agent spinner.
803             nightModeSpinner.performClick();
804         });
805         
806         // Set the pinned SSL certificate icon.
807         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.
808             // Check the switch.
809             pinnedSslCertificateSwitch.setChecked(true);
810
811             // Set the icon according to the theme.
812             if (MainWebViewActivity.darkTheme) {
813                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark));
814             } else {
815                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_light));
816             }
817         } else {  // Pinned SSL certificate is disabled.
818             // Uncheck the switch.
819             pinnedSslCertificateSwitch.setChecked(false);
820
821             // Set the icon according to the theme.
822             if (MainWebViewActivity.darkTheme) {
823                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark));
824             } else {
825                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_light));
826             }
827         }
828
829         // Store the current date.
830         Date currentDate = Calendar.getInstance().getTime();
831
832         // Setup the `StringBuilders` to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
833         savedSslCertificateIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslCertificateIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
834         savedSslCertificateIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslCertificateIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
835         savedSslCertificateIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslCertificateIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
836         savedSslCertificateIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslCertificateIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
837         savedSslCertificateIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslCertificateIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
838
839         // Check the certificate `Common Name` against the domain name.
840         boolean savedSSlCertificateCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslCertificateIssuedToCNameString);
841
842         // Format the `issuedToCommonName` color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
843         if (savedSSlCertificateCommonNameMatchesDomainName) {
844             savedSslCertificateIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslCertificateIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
845         } else {
846             savedSslCertificateIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), savedSslCertificateIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
847         }
848
849         //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
850         if ((savedSslCertificateStartDate != null) && savedSslCertificateStartDate.after(currentDate)) {  // The certificate start date is in the future.
851             savedSslCertificateStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), savedSslCertificateStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
852         } else {  // The certificate start date is in the past.
853             savedSslCertificateStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), savedSslCertificateStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
854         }
855
856         // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
857         if ((savedSslCertificateEndDate != null) && savedSslCertificateEndDate.before(currentDate)) {  // The certificate end date is in the past.
858             savedSslCertificateEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), savedSslCertificateEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
859         } else {  // The certificate end date is in the future.
860             savedSslCertificateEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), savedSslCertificateEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
861         }
862
863         // Display the current website SSL certificate strings.
864         savedSslCertificateIssuedToCNameTextView.setText(savedSslCertificateIssuedToCNameStringBuilder);
865         savedSslCertificateIssuedToONameTextView.setText(savedSslCertificateIssuedToONameStringBuilder);
866         savedSslCertificateIssuedToUNameTextView.setText(savedSslCertificateIssuedToUNameStringBuilder);
867         savedSslCertificateIssuedByCNameTextView.setText(savedSslCertificateIssuedByCNameStringBuilder);
868         savedSslCertificateIssuedByONameTextView.setText(savedSslCertificateIssuedByONameStringBuilder);
869         savedSslCertificateIssuedByUNameTextView.setText(savedSslCertificateIssuedByUNameStringBuilder);
870         savedSslCertificateStartDateTextView.setText(savedSslCertificateStartDateStringBuilder);
871         savedSslCertificateEndDateTextView.setText(savedSslCertificateEndDateStringBuilder);
872
873         // Populate the current website SSL certificate if there is one.
874         if (currentWebsiteSslCertificate != null) {
875             // Get the strings from the SSL certificate.
876             String currentWebsiteCertificateIssuedToCNameString = currentWebsiteSslCertificate.getIssuedTo().getCName();
877             String currentWebsiteCertificateIssuedToONameString = currentWebsiteSslCertificate.getIssuedTo().getOName();
878             String currentWebsiteCertificateIssuedToUNameString = currentWebsiteSslCertificate.getIssuedTo().getUName();
879             String currentWebsiteCertificateIssuedByCNameString = currentWebsiteSslCertificate.getIssuedBy().getCName();
880             String currentWebsiteCertificateIssuedByONameString = currentWebsiteSslCertificate.getIssuedBy().getOName();
881             String currentWebsiteCertificateIssuedByUNameString = currentWebsiteSslCertificate.getIssuedBy().getUName();
882             Date currentWebsiteCertificateStartDate = currentWebsiteSslCertificate.getValidNotBeforeDate();
883             Date currentWebsiteCertificateEndDate = currentWebsiteSslCertificate.getValidNotAfterDate();
884
885             // Create a `SpannableStringBuilder` for each `TextView` that needs multiple colors of text.
886             SpannableStringBuilder currentWebsiteCertificateIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentWebsiteCertificateIssuedToCNameString);
887             SpannableStringBuilder currentWebsiteCertificateIssuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentWebsiteCertificateIssuedToONameString);
888             SpannableStringBuilder currentWebsiteCertificateIssuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentWebsiteCertificateIssuedToUNameString);
889             SpannableStringBuilder currentWebsiteCertificateIssuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentWebsiteCertificateIssuedByCNameString);
890             SpannableStringBuilder currentWebsiteCertificateIssuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentWebsiteCertificateIssuedByONameString);
891             SpannableStringBuilder currentWebsiteCertificateIssuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentWebsiteCertificateIssuedByUNameString);
892             SpannableStringBuilder currentWebsiteCertificateStartDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
893                     .format(currentWebsiteCertificateStartDate));
894             SpannableStringBuilder currentWebsiteCertificateEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
895                     .format(currentWebsiteCertificateEndDate));
896
897             // Setup the `StringBuilders` to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
898             currentWebsiteCertificateIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentWebsiteCertificateIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
899             currentWebsiteCertificateIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentWebsiteCertificateIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
900             currentWebsiteCertificateIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentWebsiteCertificateIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
901             currentWebsiteCertificateIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentWebsiteCertificateIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
902             currentWebsiteCertificateIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentWebsiteCertificateIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
903
904             // Check the certificate `Common Name` against the domain name.
905             boolean currentWebsiteCertificateCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, currentWebsiteCertificateIssuedToCNameString);
906
907             // Format the `issuedToCommonName` color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
908             if (currentWebsiteCertificateCommonNameMatchesDomainName) {
909                 currentWebsiteCertificateIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentWebsiteCertificateIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
910             } else {
911                 currentWebsiteCertificateIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), currentWebsiteCertificateIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
912             }
913
914             //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
915             if (currentWebsiteCertificateStartDate.after(currentDate)) {  // The certificate start date is in the future.
916                 currentWebsiteCertificateStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), currentWebsiteCertificateStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
917             } else {  // The certificate start date is in the past.
918                 currentWebsiteCertificateStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), currentWebsiteCertificateStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
919             }
920
921             // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
922             if (currentWebsiteCertificateEndDate.before(currentDate)) {  // The certificate end date is in the past.
923                 currentWebsiteCertificateEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), currentWebsiteCertificateEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
924             } else {  // The certificate end date is in the future.
925                 currentWebsiteCertificateEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), currentWebsiteCertificateEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
926             }
927
928             // Display the current website SSL certificate strings.
929             currentWebsiteCertificateIssuedToCNameTextView.setText(currentWebsiteCertificateIssuedToCNameStringBuilder);
930             currentWebsiteCertificateIssuedToONameTextView.setText(currentWebsiteCertificateIssuedToONameStringBuilder);
931             currentWebsiteCertificateIssuedToUNameTextView.setText(currentWebsiteCertificateIssuedToUNameStringBuilder);
932             currentWebsiteCertificateIssuedByCNameTextView.setText(currentWebsiteCertificateIssuedByCNameStringBuilder);
933             currentWebsiteCertificateIssuedByONameTextView.setText(currentWebsiteCertificateIssuedByONameStringBuilder);
934             currentWebsiteCertificateIssuedByUNameTextView.setText(currentWebsiteCertificateIssuedByUNameStringBuilder);
935             currentWebsiteCertificateStartDateTextView.setText(currentWebsiteCertificateStartDateStringBuilder);
936             currentWebsiteCertificateEndDateTextView.setText(currentWebsiteCertificateEndDateStringBuilder);
937         }
938
939         // Set the initial display status for the SSL certificates.
940         if (pinnedSslCertificateSwitch.isChecked()) {
941             // Set the visibility of the saved SSL certificate.
942             if (savedSslCertificateIssuedToCNameString == null) {
943                 savedSslCertificateLinearLayout.setVisibility(View.GONE);
944             } else {
945                 savedSslCertificateLinearLayout.setVisibility(View.VISIBLE);
946             }
947
948             // Set the visibility of the current website SSL certificate.
949             if (currentWebsiteSslCertificate == null) {
950                 // Hide the SSL certificate.
951                 currentWebsiteCertificateLinearLayout.setVisibility(View.GONE);
952
953                 // Show the instruction.
954                 noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
955             } else {
956                 // Show the SSL certificate.
957                 currentWebsiteCertificateLinearLayout.setVisibility(View.VISIBLE);
958
959                 // Hide the instruction.
960                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
961             }
962
963             // Set the status of the radio buttons.
964             if (savedSslCertificateLinearLayout.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
965                 savedSslCertificateRadioButton.setChecked(true);
966                 currentWebsiteCertificateRadioButton.setChecked(false);
967             } else if (currentWebsiteCertificateLinearLayout.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
968                 currentWebsiteCertificateRadioButton.setChecked(true);
969                 savedSslCertificateRadioButton.setChecked(false);
970             } else {  // Neither SSL certificate is visible.
971                 savedSslCertificateRadioButton.setChecked(false);
972                 currentWebsiteCertificateRadioButton.setChecked(false);
973             }
974         } else {  // `pinnedSslCertificateSwitch` is not checked.
975             // Hide the SSl certificates and instructions.
976             savedSslCertificateLinearLayout.setVisibility(View.GONE);
977             currentWebsiteCertificateLinearLayout.setVisibility(View.GONE);
978             noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
979
980             // Uncheck the radio buttons.
981             savedSslCertificateRadioButton.setChecked(false);
982             currentWebsiteCertificateRadioButton.setChecked(false);
983         }
984
985
986         // Set the JavaScript switch listener.
987         javaScriptEnabledSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
988             if (isChecked) {  // JavaScript is enabled.
989                 // Update the JavaScript icon.
990                 javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled));
991
992                 // Enable the DOM storage `Switch`.
993                 domStorageEnabledSwitch.setEnabled(true);
994
995                 // Update the DOM storage icon.
996                 if (domStorageEnabledSwitch.isChecked()) {  // DOM storage is enabled.
997                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
998                 } else {  // DOM storage is disabled.
999                     // Set the icon according to the theme.
1000                     if (MainWebViewActivity.darkTheme) {
1001                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
1002                     } else {
1003                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
1004                     }
1005                 }
1006             } else {  // JavaScript is disabled.
1007                 // Update the JavaScript icon.
1008                 javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode));
1009
1010                 // Disable the DOM storage `Switch`.
1011                 domStorageEnabledSwitch.setEnabled(false);
1012
1013                 // Set the DOM storage icon according to the theme.
1014                 if (MainWebViewActivity.darkTheme) {
1015                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_dark));
1016                 } else {
1017                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_light));
1018                 }
1019             }
1020         });
1021
1022         // Set the first-party cookies switch listener.
1023         firstPartyCookiesEnabledSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1024             if (isChecked) {  // First-party cookies are enabled.
1025                 // Update the first-party cookies icon.
1026                 firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_enabled));
1027
1028                 // Enable the third-party cookies switch.
1029                 thirdPartyCookiesEnabledSwitch.setEnabled(true);
1030
1031                 // Update the third-party cookies icon.
1032                 if (thirdPartyCookiesEnabledSwitch.isChecked()) {  // Third-party cookies are enabled.
1033                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
1034                 } else {  // Third-party cookies are disabled.
1035                     // Set the third-party cookies icon according to the theme.
1036                     if (MainWebViewActivity.darkTheme) {
1037                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
1038                     } else {
1039                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
1040                     }
1041                 }
1042             } else {  // First-party cookies are disabled.
1043                 // Update the first-party cookies icon according to the theme.
1044                 if (MainWebViewActivity.darkTheme) {
1045                     firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
1046                 } else {
1047                     firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
1048                 }
1049
1050                 // Disable the third-party cookies switch.
1051                 thirdPartyCookiesEnabledSwitch.setEnabled(false);
1052
1053                 // Set the third-party cookies icon according to the theme.
1054                 if (MainWebViewActivity.darkTheme) {
1055                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_dark));
1056                 } else {
1057                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_light));
1058                 }
1059             }
1060         });
1061
1062         // Set the third-party cookies switch listener.
1063         thirdPartyCookiesEnabledSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1064             // Update the icon.
1065             if (isChecked) {
1066                 thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
1067             } else {
1068                 // Update the third-party cookies icon according to the theme.
1069                 if (MainWebViewActivity.darkTheme) {
1070                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
1071                 } else {
1072                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
1073                 }
1074             }
1075         });
1076
1077         // Set the DOM Storage switch listener.
1078         domStorageEnabledSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1079             // Update the icon.
1080             if (isChecked) {
1081                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
1082             } else {
1083                 // Set the icon according to the theme.
1084                 if (MainWebViewActivity.darkTheme) {
1085                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
1086                 } else {
1087                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
1088                 }
1089             }
1090         });
1091
1092         // Set the form data switch listener.
1093         formDataEnabledSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1094             // Update the icon.
1095             if (isChecked) {
1096                 formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_enabled));
1097             } else {
1098                 // Set the icon according to the theme.
1099                 if (MainWebViewActivity.darkTheme) {
1100                     formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_dark));
1101                 } else {
1102                     formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_light));
1103                 }
1104             }
1105         });
1106
1107         // Set the EasyList switch listener.
1108         easyListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1109             // Update the icon.
1110             if (isChecked) {  // EasyList is on.
1111                 // Set the icon according to the theme.
1112                 if (MainWebViewActivity.darkTheme) {
1113                     easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_dark));
1114                 } else {
1115                     easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_light));
1116                 }
1117             } else {  // EasyList is off.
1118                 // Set the icon according to the theme.
1119                 if (MainWebViewActivity.darkTheme) {
1120                     easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_dark));
1121                 } else {
1122                     easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_light));
1123                 }
1124             }
1125         });
1126
1127         // Set the EasyPrivacy switch listener.
1128         easyPrivacySwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1129             // Update the icon.
1130             if (isChecked) {  // EasyPrivacy is on.
1131                 // Set the icon according to the theme.
1132                 if (MainWebViewActivity.darkTheme) {
1133                     easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_dark));
1134                 } else {
1135                     easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_light));
1136                 }
1137             } else {  // EasyPrivacy is off.
1138                 // Set the icon according to the theme.
1139                 if (MainWebViewActivity.darkTheme) {
1140                     easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_dark));
1141                 } else {
1142                     easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_light));
1143                 }
1144             }
1145         });
1146
1147         // Set the Fanboy's Annoyance List switch listener.
1148         fanboysAnnoyanceListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1149             // Update the icon and Fanboy's Social Blocking List.
1150             if (isChecked) {  // Fanboy's Annoyance List is on.
1151                 // Set the icon according to the theme.
1152                 if (MainWebViewActivity.darkTheme) {
1153                     fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
1154                 } else {
1155                     fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
1156                 }
1157
1158                 // Disable the Fanboy's Social Blocking List switch.
1159                 fanboysSocialBlockingListSwitch.setEnabled(false);
1160
1161                 // Update the Fanboy's Social Blocking List icon according to the theme.
1162                 if (MainWebViewActivity.darkTheme) {
1163                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_dark));
1164                 } else {
1165                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_light));
1166                 }
1167             } else {  // Fanboy's Annoyance List is off.
1168                 // Set the icon according to the theme.
1169                 if (MainWebViewActivity.darkTheme) {
1170                     fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
1171                 } else {
1172                     fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
1173                 }
1174
1175                 // Enable the Fanboy's Social Blocking List switch.
1176                 fanboysSocialBlockingListSwitch.setEnabled(true);
1177
1178                 // Update the Fanboy's Social Blocking List icon.
1179                 if (fanboysSocialBlockingListSwitch.isChecked()) {  // Fanboy's Social Blocking List is on.
1180                     // Update the icon according to the theme.
1181                     if (MainWebViewActivity.darkTheme) {
1182                         fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
1183                     } else {
1184                         fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
1185                     }
1186                 } else {  // Fanboy's Social Blocking List is off.
1187                     // Update the icon according to the theme.
1188                     if (MainWebViewActivity.darkTheme) {
1189                         fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
1190                     } else {
1191                         fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
1192                     }
1193                 }
1194             }
1195
1196         });
1197
1198         // Set the Fanboy's Social Blocking List switch listener.
1199         fanboysSocialBlockingListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1200             // Update the icon.
1201             if (isChecked) {  // Fanboy's Social Blocking List is on.
1202                 // Set the icon according to the theme.
1203                 if (MainWebViewActivity.darkTheme) {
1204                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
1205                 } else {
1206                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
1207                 }
1208             } else {  // Fanboy's Social Blocking List is off.
1209                 // Set the icon according to the theme.
1210                 if (MainWebViewActivity.darkTheme) {
1211                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
1212                 } else {
1213                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
1214                 }
1215             }
1216         });
1217
1218         // Set the user agent spinner listener.
1219         userAgentSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1220             @Override
1221             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1222                 // Set the new user agent.
1223                 switch (position) {
1224                     case MainWebViewActivity.DOMAINS_SYSTEM_DEFAULT_USER_AGENT:
1225                         // Show the user agent TextView.
1226                         userAgentTextView.setVisibility(View.VISIBLE);
1227
1228                         // Hide the custom user agent EditText.
1229                         customUserAgentEditText.setVisibility(View.GONE);
1230
1231                         // Set the user text.
1232                         switch (defaultUserAgentArrayPosition) {
1233                             case MainWebViewActivity.UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
1234                                 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
1235                                 userAgentTextView.setText(defaultUserAgentName);
1236                                 break;
1237
1238                             case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
1239                                 // Display the `WebView` default user agent.
1240                                 userAgentTextView.setText(webViewDefaultUserAgentString);
1241                                 break;
1242
1243                             case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
1244                                 // Display the custom user agent.
1245                                 userAgentTextView.setText(defaultCustomUserAgentString);
1246                                 break;
1247
1248                             default:
1249                                 // Get the user agent string from the user agent data array.
1250                                 userAgentTextView.setText(userAgentDataArray[defaultUserAgentArrayPosition]);
1251                         }
1252                         break;
1253
1254                     case MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT:
1255                         // Show the user agent TextView and set the text.
1256                         userAgentTextView.setVisibility(View.VISIBLE);
1257                         userAgentTextView.setText(webViewDefaultUserAgentString);
1258
1259                         // Hide the custom user agent EditTex.
1260                         customUserAgentEditText.setVisibility(View.GONE);
1261                         break;
1262
1263                     case MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT:
1264                         // Hide the user agent TextView.
1265                         userAgentTextView.setVisibility(View.GONE);
1266
1267                         // Show the custom user agent EditText and set the current user agent name as the text.
1268                         customUserAgentEditText.setVisibility(View.VISIBLE);
1269                         customUserAgentEditText.setText(currentUserAgentName);
1270                         break;
1271
1272                     default:
1273                         // 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.
1274                         userAgentTextView.setVisibility(View.VISIBLE);
1275                         userAgentTextView.setText(userAgentDataArray[position - 1]);
1276
1277                         // Hide `customUserAgentEditText`.
1278                         customUserAgentEditText.setVisibility(View.GONE);
1279                 }
1280             }
1281
1282             @Override
1283             public void onNothingSelected(AdapterView<?> parent) {
1284                 // Do nothing.
1285             }
1286         });
1287
1288         // Set the font size spinner listener.
1289         fontSizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1290             @Override
1291             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1292                 // Update the display options for `fontSizeTextView`.
1293                 if (position == 0) {  // System default font size has been selected.  Display `fontSizeTextView`.
1294                     fontSizeTextView.setVisibility(View.VISIBLE);
1295                 } else {  // A custom font size has been selected.  Hide `fontSizeTextView`.
1296                     fontSizeTextView.setVisibility(View.GONE);
1297                 }
1298             }
1299
1300             @Override
1301             public void onNothingSelected(AdapterView<?> parent) {
1302                 // Do nothing.
1303             }
1304         });
1305
1306         // Set the display webpage images spinner listener.
1307         displayWebpageImagesSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1308             @Override
1309             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1310                 // Update the icon and the visibility of `displayImagesTextView`.
1311                 switch (position) {
1312                     case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_SYSTEM_DEFAULT:
1313                         if (defaultDisplayWebpageImagesBoolean) {
1314                             // Set the icon according to the theme.
1315                             if (MainWebViewActivity.darkTheme) {
1316                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
1317                             } else {
1318                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
1319                             }
1320                         } else {
1321                             // Set the icon according to the theme.
1322                             if (MainWebViewActivity.darkTheme) {
1323                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
1324                             } else {
1325                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
1326                             }
1327                         }
1328
1329                         // Show `displayImagesTextView`.
1330                         displayImagesTextView.setVisibility(View.VISIBLE);
1331                         break;
1332
1333                     case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED:
1334                         // Set the icon according to the theme.
1335                         if (MainWebViewActivity.darkTheme) {
1336                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
1337                         } else {
1338                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
1339                         }
1340
1341                         // Hide `displayImagesTextView`.
1342                         displayImagesTextView.setVisibility(View.GONE);
1343                         break;
1344
1345                     case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED:
1346                         // Set the icon according to the theme.
1347                         if (MainWebViewActivity.darkTheme) {
1348                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
1349                         } else {
1350                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
1351                         }
1352
1353                         // Hide `displayImagesTextView`.
1354                         displayImagesTextView.setVisibility(View.GONE);
1355                         break;
1356                 }
1357             }
1358
1359             @Override
1360             public void onNothingSelected(AdapterView<?> parent) {
1361                 // Do nothing.
1362             }
1363         });
1364
1365         // Set the night mode spinner listener.
1366         nightModeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1367             @Override
1368             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1369                 // 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.
1370                 switch (position) {
1371                     case DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT:
1372                         if (defaultNightModeBoolean) {  // Night mode enabled by default.
1373                             // Set the icon according to the theme.
1374                             if (MainWebViewActivity.darkTheme) {
1375                                 nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark));
1376                             } else {
1377                                 nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light));
1378                             }
1379                         } else {  // Night mode disabled by default.
1380                             // Set the icon according to the theme.
1381                             if (MainWebViewActivity.darkTheme) {
1382                                 nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark));
1383                             } else {
1384                                 nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light));
1385                             }
1386                         }
1387
1388                         // Show `nightModeTextView`.
1389                         nightModeTextView.setVisibility(View.VISIBLE);
1390                         break;
1391
1392                     case DomainsDatabaseHelper.NIGHT_MODE_ENABLED:
1393                         // Set the icon according to the theme.
1394                         if (MainWebViewActivity.darkTheme) {
1395                             nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark));
1396                         } else {
1397                             nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light));
1398                         }
1399
1400                         // Hide `nightModeTextView`.
1401                         nightModeTextView.setVisibility(View.GONE);
1402                         break;
1403
1404                     case DomainsDatabaseHelper.NIGHT_MODE_DISABLED:
1405                         // Set the icon according to the theme.
1406                         if (MainWebViewActivity.darkTheme) {
1407                             nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark));
1408                         } else {
1409                             nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light));
1410                         }
1411
1412                         // Hide `nightModeTextView`.
1413                         nightModeTextView.setVisibility(View.GONE);
1414                         break;
1415                 }
1416
1417                 // Create a `boolean` to store the current night mode setting.
1418                 boolean currentNightModeEnabled = (position == DomainsDatabaseHelper.NIGHT_MODE_ENABLED) || ((position == DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT) && defaultNightModeBoolean);
1419
1420                 // Disable the JavaScript `Switch` if night mode is enabled.
1421                 if (currentNightModeEnabled) {
1422                     javaScriptEnabledSwitch.setEnabled(false);
1423                 } else {
1424                     javaScriptEnabledSwitch.setEnabled(true);
1425                 }
1426
1427                 // Update the JavaScript icon.
1428                 if ((javaScriptEnabledInt == 1) || currentNightModeEnabled) {
1429                     javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled));
1430                 } else {
1431                     javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode));
1432                 }
1433
1434                 // Update the DOM storage status.
1435                 if ((javaScriptEnabledInt == 1) || currentNightModeEnabled) {  // JavaScript is enabled.
1436                     // Enable the DOM storage `Switch`.
1437                     domStorageEnabledSwitch.setEnabled(true);
1438
1439                     // Set the DOM storage status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
1440                     if (domStorageEnabledInt == 1) {  // Both JavaScript and DOM storage are enabled.
1441                         domStorageEnabledSwitch.setChecked(true);
1442                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
1443                     } else {  // JavaScript is enabled but DOM storage is disabled.
1444                         // Set the DOM storage switch to off.
1445                         domStorageEnabledSwitch.setChecked(false);
1446
1447                         // Set the icon according to the theme.
1448                         if (MainWebViewActivity.darkTheme) {
1449                             domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
1450                         } else {
1451                             domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
1452                         }
1453                     }
1454                 } else {  // JavaScript is disabled.
1455                     // Disable the DOM storage `Switch`.
1456                     domStorageEnabledSwitch.setEnabled(false);
1457
1458                     // Set the checked status of DOM storage.
1459                     if (domStorageEnabledInt == 1) {  // DOM storage is enabled but JavaScript is disabled.
1460                         domStorageEnabledSwitch.setChecked(true);
1461                     } else {  // Both JavaScript and DOM storage are disabled.
1462                         domStorageEnabledSwitch.setChecked(false);
1463                     }
1464
1465                     // Set the icon according to the theme.
1466                     if (MainWebViewActivity.darkTheme) {
1467                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_dark));
1468                     } else {
1469                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_light));
1470                     }
1471                 }
1472             }
1473
1474             @Override
1475             public void onNothingSelected(AdapterView<?> parent) {
1476                 // Do nothing.
1477             }
1478         });
1479         
1480         // Set the pinned SSL certificate switch listener.
1481         pinnedSslCertificateSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1482             // Update the icon
1483             if (isChecked) {  // Pinned SSL certificate is enabled.
1484                 // Set the icon according to the theme.
1485                 if (MainWebViewActivity.darkTheme) {
1486                     pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark));
1487                 } else {
1488                     pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_light));
1489                 }
1490
1491                 // Update the visibility of the saved SSL certificate.
1492                 if (savedSslCertificateIssuedToCNameString == null) {
1493                     savedSslCertificateLinearLayout.setVisibility(View.GONE);
1494                 } else {
1495                     savedSslCertificateLinearLayout.setVisibility(View.VISIBLE);
1496                 }
1497
1498                 // Update the visibility of the current website SSL certificate.
1499                 if (currentWebsiteSslCertificate == null) {
1500                     // Hide the SSL certificate.
1501                     currentWebsiteCertificateLinearLayout.setVisibility(View.GONE);
1502
1503                     // Show the instruction.
1504                     noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
1505                 } else {
1506                     // Show the SSL certificate.
1507                     currentWebsiteCertificateLinearLayout.setVisibility(View.VISIBLE);
1508
1509                     // Hide the instruction.
1510                     noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1511                 }
1512
1513                 // Set the status of the radio buttons.
1514                 if (savedSslCertificateLinearLayout.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
1515                     savedSslCertificateRadioButton.setChecked(true);
1516                     currentWebsiteCertificateRadioButton.setChecked(false);
1517                 } else if (currentWebsiteCertificateLinearLayout.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1518                     currentWebsiteCertificateRadioButton.setChecked(true);
1519                     savedSslCertificateRadioButton.setChecked(false);
1520                 } else {  // Neither SSL certificate is visible.
1521                     savedSslCertificateRadioButton.setChecked(false);
1522                     currentWebsiteCertificateRadioButton.setChecked(false);
1523                 }
1524             } else {  // Pinned SSL certificate is disabled.
1525                 // Set the icon according to the theme.
1526                 if (MainWebViewActivity.darkTheme) {
1527                     pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark));
1528                 } else {
1529                     pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_light));
1530                 }
1531
1532                 // Hide the SSl certificates and instructions.
1533                 savedSslCertificateLinearLayout.setVisibility(View.GONE);
1534                 currentWebsiteCertificateLinearLayout.setVisibility(View.GONE);
1535                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1536
1537                 // Uncheck the radio buttons.
1538                 savedSslCertificateRadioButton.setChecked(false);
1539                 currentWebsiteCertificateRadioButton.setChecked(false);
1540             }
1541         });
1542
1543         savedSslCertificateLinearLayout.setOnClickListener((View v) -> {
1544             savedSslCertificateRadioButton.setChecked(true);
1545             currentWebsiteCertificateRadioButton.setChecked(false);
1546         });
1547
1548         savedSslCertificateRadioButton.setOnClickListener((View v) -> {
1549             savedSslCertificateRadioButton.setChecked(true);
1550             currentWebsiteCertificateRadioButton.setChecked(false);
1551         });
1552
1553         currentWebsiteCertificateLinearLayout.setOnClickListener((View v) -> {
1554             currentWebsiteCertificateRadioButton.setChecked(true);
1555             savedSslCertificateRadioButton.setChecked(false);
1556         });
1557
1558         currentWebsiteCertificateRadioButton.setOnClickListener((View v) -> {
1559             currentWebsiteCertificateRadioButton.setChecked(true);
1560             savedSslCertificateRadioButton.setChecked(false);
1561         });
1562
1563         return domainSettingsView;
1564     }
1565
1566     private boolean checkDomainNameAgainstCertificate(String domainName, String certificateCommonName) {
1567         // Initialize `domainNamesMatch`.
1568         boolean domainNamesMatch = false;
1569
1570         // Check if the domains match.
1571         if (domainName.equals(certificateCommonName)) {
1572             domainNamesMatch = true;
1573         }
1574
1575         // Check various wildcard permutations if `domainName` and `certificateCommonName` are not empty.
1576         // `noinspection ConstantCondition` removes Android Studio's incorrect lint warning that `domainName` can never be `null`.
1577         //noinspection ConstantConditions
1578         if ((domainName != null) && (certificateCommonName != null)) {
1579             // If `domainName` starts with a wildcard, check the base domain against all the subdomains of `certificateCommonName`.
1580             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2)) {
1581                 // Remove the initial `*.`.
1582                 String baseDomainName = domainName.substring(2);
1583
1584                 // Setup a copy of `certificateCommonName` to test subdomains.
1585                 String certificateCommonNameSubdomain = certificateCommonName;
1586
1587                 // Check all the subdomains in `certificateCommonNameSubdomain` against `baseDomainName`.
1588                 while (!domainNamesMatch && certificateCommonNameSubdomain.contains(".")) {  // Stop checking if we know that `domainNamesMatch` is `true` or if we run out of  `.`.
1589                     // Test the `certificateCommonNameSubdomain` against `baseDomainName`.
1590                     if (certificateCommonNameSubdomain.equals(baseDomainName)) {
1591                         domainNamesMatch = true;
1592                     }
1593
1594                     // Strip out the lowest subdomain of `certificateCommonNameSubdomain`.
1595                     try {
1596                         certificateCommonNameSubdomain = certificateCommonNameSubdomain.substring(certificateCommonNameSubdomain.indexOf(".") + 1);
1597                     } catch (IndexOutOfBoundsException e) {  // `certificateCommonNameSubdomain` ends with `.`.
1598                         certificateCommonNameSubdomain = "";
1599                     }
1600                 }
1601             }
1602
1603             // If `certificateCommonName` starts with a wildcard, check the base common name against all the subdomains of `domainName`.
1604             if (!domainNamesMatch && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
1605                 // Remove the initial `*.`.
1606                 String baseCertificateCommonName = certificateCommonName.substring(2);
1607
1608                 // Setup a copy of `domainName` to test subdomains.
1609                 String domainNameSubdomain = domainName;
1610
1611                 // Check all the subdomains in `domainNameSubdomain` against `baseCertificateCommonName`.
1612                 while (!domainNamesMatch && domainNameSubdomain.contains(".") && (domainNameSubdomain.length() > 2)) {
1613                     // Test the `domainNameSubdomain` against `baseCertificateCommonName`.
1614                     if (domainNameSubdomain.equals(baseCertificateCommonName)) {
1615                         domainNamesMatch = true;
1616                     }
1617
1618                     // Strip out the lowest subdomain of `domainNameSubdomain`.
1619                     try {
1620                         domainNameSubdomain = domainNameSubdomain.substring(domainNameSubdomain.indexOf(".") + 1);
1621                     } catch (IndexOutOfBoundsException e) { // `domainNameSubdomain` ends with `.`.
1622                         domainNameSubdomain = "";
1623                     }
1624                 }
1625             }
1626
1627             // If both names start with a wildcard, check if the root of one contains the root of the other.
1628             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2) && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
1629                 // Remove the wildcards.
1630                 String rootDomainName = domainName.substring(2);
1631                 String rootCertificateCommonName = certificateCommonName.substring(2);
1632
1633                 // Check if one name ends with the contents of the other.  If so, there will be overlap in the their wildcard subdomains.
1634                 if (rootDomainName.endsWith(rootCertificateCommonName) || rootCertificateCommonName.endsWith(rootDomainName)) {
1635                     domainNamesMatch = true;
1636                 }
1637             }
1638         }
1639
1640         return domainNamesMatch;
1641     }
1642 }