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