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