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