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