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