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