Allow specifying any font size. https://redmine.stoutner.com/issues/504
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / fragments / DomainSettingsFragment.java
1 /*
2  * Copyright © 2017-2019 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.os.Build;
28 import android.os.Bundle;
29 import android.preference.PreferenceManager;
30 import android.text.Editable;
31 import android.text.SpannableStringBuilder;
32 import android.text.Spanned;
33 import android.text.TextWatcher;
34 import android.text.style.ForegroundColorSpan;
35 import android.view.LayoutInflater;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.webkit.WebView;
39 import android.widget.AdapterView;
40 import android.widget.ArrayAdapter;
41 import android.widget.CompoundButton;
42 import android.widget.EditText;
43 import android.widget.ImageView;
44 import android.widget.LinearLayout;
45 import android.widget.RadioButton;
46 import android.widget.Spinner;
47 import android.widget.Switch;
48 import android.widget.TextView;
49
50 import androidx.annotation.NonNull;
51 import androidx.cardview.widget.CardView;
52 import androidx.fragment.app.Fragment;  // The AndroidX fragment must be used until minimum API >= 23.  Otherwise `getContext()` does not work.
53
54 import com.stoutner.privacybrowser.R;
55 import com.stoutner.privacybrowser.activities.DomainsActivity;
56 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
57 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
58
59 import java.text.DateFormat;
60 import java.util.Calendar;
61 import java.util.Date;
62
63 public class DomainSettingsFragment extends Fragment {
64     // `DATABASE_ID` is used by activities calling this fragment.
65     public static final String DATABASE_ID = "database_id";
66
67     // `databaseId` is public static so it can be accessed from `DomainsActivity`. It is also used in `onCreate()` and `onCreateView()`.
68     public static int databaseId;
69
70     @Override
71     public void onCreate(Bundle savedInstanceState) {
72         // Run the default commands.
73         super.onCreate(savedInstanceState);
74
75         // Remove the lint warning that `getArguments` might be null.
76         assert getArguments() != null;
77
78         // Store the database id in `databaseId`.
79         databaseId = getArguments().getInt(DATABASE_ID);
80     }
81
82     // The deprecated `getDrawable()` must be used until the minimum API >= 21.
83     @Override
84     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
85         // Inflate `domain_settings_fragment`.  `false` does not attach it to the root `container`.
86         View domainSettingsView = inflater.inflate(R.layout.domain_settings_fragment, container, false);
87
88         // Get a handle for the context and the resources.
89         Context context = getContext();
90         Resources resources = getResources();
91
92         // Remove the error below that the context might be null.
93         assert context != null;
94
95         // Get a handle for the shared preference.
96         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
97
98         // Store the default settings.
99         String defaultUserAgentName = sharedPreferences.getString("user_agent", getString(R.string.user_agent_default_value));
100         String defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value));
101         String defaultFontSizeString = sharedPreferences.getString("font_size", getString(R.string.font_size_default_value));
102         boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
103         boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
104         boolean defaultNightMode = sharedPreferences.getBoolean("night_mode", false);
105         boolean defaultWideViewport = sharedPreferences.getBoolean("wide_viewport", true);
106         boolean defaultDisplayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true);
107
108         // Get handles for the views in the fragment.
109         EditText domainNameEditText = domainSettingsView.findViewById(R.id.domain_settings_name_edittext);
110         ImageView javaScriptImageView = domainSettingsView.findViewById(R.id.javascript_imageview);
111         Switch javaScriptSwitch = domainSettingsView.findViewById(R.id.javascript_switch);
112         ImageView firstPartyCookiesImageView = domainSettingsView.findViewById(R.id.first_party_cookies_imageview);
113         Switch firstPartyCookiesSwitch = domainSettingsView.findViewById(R.id.first_party_cookies_switch);
114         LinearLayout thirdPartyCookiesLinearLayout = domainSettingsView.findViewById(R.id.third_party_cookies_linearlayout);
115         ImageView thirdPartyCookiesImageView = domainSettingsView.findViewById(R.id.third_party_cookies_imageview);
116         Switch thirdPartyCookiesSwitch = domainSettingsView.findViewById(R.id.third_party_cookies_switch);
117         ImageView domStorageImageView = domainSettingsView.findViewById(R.id.dom_storage_imageview);
118         Switch domStorageSwitch = domainSettingsView.findViewById(R.id.dom_storage_switch);
119         ImageView formDataImageView = domainSettingsView.findViewById(R.id.form_data_imageview);  // The form data views can be remove once the minimum API >= 26.
120         Switch formDataSwitch = domainSettingsView.findViewById(R.id.form_data_switch);  // The form data views can be remove once the minimum API >= 26.
121         ImageView easyListImageView = domainSettingsView.findViewById(R.id.easylist_imageview);
122         Switch easyListSwitch = domainSettingsView.findViewById(R.id.easylist_switch);
123         ImageView easyPrivacyImageView = domainSettingsView.findViewById(R.id.easyprivacy_imageview);
124         Switch easyPrivacySwitch = domainSettingsView.findViewById(R.id.easyprivacy_switch);
125         ImageView fanboysAnnoyanceListImageView = domainSettingsView.findViewById(R.id.fanboys_annoyance_list_imageview);
126         Switch fanboysAnnoyanceListSwitch = domainSettingsView.findViewById(R.id.fanboys_annoyance_list_switch);
127         ImageView fanboysSocialBlockingListImageView = domainSettingsView.findViewById(R.id.fanboys_social_blocking_list_imageview);
128         Switch fanboysSocialBlockingListSwitch = domainSettingsView.findViewById(R.id.fanboys_social_blocking_list_switch);
129         ImageView ultraListImageView = domainSettingsView.findViewById(R.id.ultralist_imageview);
130         Switch ultraListSwitch = domainSettingsView.findViewById(R.id.ultralist_switch);
131         ImageView ultraPrivacyImageView = domainSettingsView.findViewById(R.id.ultraprivacy_imageview);
132         Switch ultraPrivacySwitch = domainSettingsView.findViewById(R.id.ultraprivacy_switch);
133         ImageView blockAllThirdPartyRequestsImageView = domainSettingsView.findViewById(R.id.block_all_third_party_requests_imageview);
134         Switch blockAllThirdPartyRequestsSwitch = domainSettingsView.findViewById(R.id.block_all_third_party_requests_switch);
135         Spinner userAgentSpinner = domainSettingsView.findViewById(R.id.user_agent_spinner);
136         TextView userAgentTextView = domainSettingsView.findViewById(R.id.user_agent_textview);
137         EditText customUserAgentEditText = domainSettingsView.findViewById(R.id.custom_user_agent_edittext);
138         Spinner fontSizeSpinner = domainSettingsView.findViewById(R.id.font_size_spinner);
139         TextView defaultFontSizeTextView = domainSettingsView.findViewById(R.id.default_font_size_textview);
140         EditText customFontSizeEditText = domainSettingsView.findViewById(R.id.custom_font_size_edittext);
141         ImageView swipeToRefreshImageView = domainSettingsView.findViewById(R.id.swipe_to_refresh_imageview);
142         Spinner swipeToRefreshSpinner = domainSettingsView.findViewById(R.id.swipe_to_refresh_spinner);
143         TextView swipeToRefreshTextView = domainSettingsView.findViewById(R.id.swipe_to_refresh_textview);
144         ImageView nightModeImageView = domainSettingsView.findViewById(R.id.night_mode_imageview);
145         Spinner nightModeSpinner = domainSettingsView.findViewById(R.id.night_mode_spinner);
146         TextView nightModeTextView = domainSettingsView.findViewById(R.id.night_mode_textview);
147         ImageView wideViewportImageView = domainSettingsView.findViewById(R.id.wide_viewport_imageview);
148         Spinner wideViewportSpinner = domainSettingsView.findViewById(R.id.wide_viewport_spinner);
149         TextView wideViewportTextView = domainSettingsView.findViewById(R.id.wide_viewport_textview);
150         ImageView displayWebpageImagesImageView = domainSettingsView.findViewById(R.id.display_webpage_images_imageview);
151         Spinner displayWebpageImagesSpinner = domainSettingsView.findViewById(R.id.display_webpage_images_spinner);
152         TextView displayImagesTextView = domainSettingsView.findViewById(R.id.display_webpage_images_textview);
153         ImageView pinnedSslCertificateImageView = domainSettingsView.findViewById(R.id.pinned_ssl_certificate_imageview);
154         Switch pinnedSslCertificateSwitch = domainSettingsView.findViewById(R.id.pinned_ssl_certificate_switch);
155         CardView savedSslCardView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_cardview);
156         LinearLayout savedSslCertificateLinearLayout = domainSettingsView.findViewById(R.id.saved_ssl_certificate_linearlayout);
157         RadioButton savedSslCertificateRadioButton = domainSettingsView.findViewById(R.id.saved_ssl_certificate_radiobutton);
158         TextView savedSslIssuedToCNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_cname);
159         TextView savedSslIssuedToONameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_oname);
160         TextView savedSslIssuedToUNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_uname);
161         TextView savedSslIssuedByCNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_cname);
162         TextView savedSslIssuedByONameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_oname);
163         TextView savedSslIssuedByUNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_uname);
164         TextView savedSslStartDateTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_start_date);
165         TextView savedSslEndDateTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_end_date);
166         CardView currentSslCardView = domainSettingsView.findViewById(R.id.current_website_certificate_cardview);
167         LinearLayout currentWebsiteCertificateLinearLayout = domainSettingsView.findViewById(R.id.current_website_certificate_linearlayout);
168         RadioButton currentWebsiteCertificateRadioButton = domainSettingsView.findViewById(R.id.current_website_certificate_radiobutton);
169         TextView currentSslIssuedToCNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_cname);
170         TextView currentSslIssuedToONameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_oname);
171         TextView currentSslIssuedToUNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_uname);
172         TextView currentSslIssuedByCNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_cname);
173         TextView currentSslIssuedByONameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_oname);
174         TextView currentSslIssuedByUNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_uname);
175         TextView currentSslStartDateTextView = domainSettingsView.findViewById(R.id.current_website_certificate_start_date);
176         TextView currentSslEndDateTextView = domainSettingsView.findViewById(R.id.current_website_certificate_end_date);
177         TextView noCurrentWebsiteCertificateTextView = domainSettingsView.findViewById(R.id.no_current_website_certificate);
178         ImageView pinnedIpAddressesImageView = domainSettingsView.findViewById(R.id.pinned_ip_addresses_imageview);
179         Switch pinnedIpAddressesSwitch = domainSettingsView.findViewById(R.id.pinned_ip_addresses_switch);
180         CardView savedIpAddressesCardView = domainSettingsView.findViewById(R.id.saved_ip_addresses_cardview);
181         LinearLayout savedIpAddressesLinearLayout = domainSettingsView.findViewById(R.id.saved_ip_addresses_linearlayout);
182         RadioButton savedIpAddressesRadioButton = domainSettingsView.findViewById(R.id.saved_ip_addresses_radiobutton);
183         TextView savedIpAddressesTextView = domainSettingsView.findViewById(R.id.saved_ip_addresses_textview);
184         CardView currentIpAddressesCardView = domainSettingsView.findViewById(R.id.current_ip_addresses_cardview);
185         LinearLayout currentIpAddressesLinearLayout = domainSettingsView.findViewById(R.id.current_ip_addresses_linearlayout);
186         RadioButton currentIpAddressesRadioButton = domainSettingsView.findViewById(R.id.current_ip_addresses_radiobutton);
187         TextView currentIpAddressesTextView = domainSettingsView.findViewById(R.id.current_ip_addresses_textview);
188
189         // Setup the pinned labels.
190         String cNameLabel = getString(R.string.common_name) + "  ";
191         String oNameLabel = getString(R.string.organization) + "  ";
192         String uNameLabel = getString(R.string.organizational_unit) + "  ";
193         String startDateLabel = getString(R.string.start_date) + "  ";
194         String endDateLabel = getString(R.string.end_date) + "  ";
195
196         // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
197         DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0);
198
199         // Get the database cursor for this ID and move it to the first row.
200         Cursor domainCursor = domainsDatabaseHelper.getCursorForId(databaseId);
201         domainCursor.moveToFirst();
202
203         // Save the cursor entries as variables.
204         String domainNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME));
205         int javaScriptInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT));
206         int firstPartyCookiesInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES));
207         int thirdPartyCookiesInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES));
208         int domStorageInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE));
209         int formDataInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA));  // Form data can be remove once the minimum API >= 26.
210         int easyListInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST));
211         int easyPrivacyInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY));
212         int fanboysAnnoyanceListInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST));
213         int fanboysSocialBlockingListInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST));
214         int ultraListInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ULTRALIST));
215         int ultraPrivacyInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY));
216         int blockAllThirdPartyRequestsInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS));
217         String currentUserAgentName = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
218         int fontSizeInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
219         int swipeToRefreshInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
220         int nightModeInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE));
221         int wideViewportInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.WIDE_VIEWPORT));
222         int displayImagesInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
223         int pinnedSslCertificateInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE));
224         String savedSslIssuedToCNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME));
225         String savedSslIssuedToONameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION));
226         String savedSslIssuedToUNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT));
227         String savedSslIssuedByCNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME));
228         String savedSslIssuedByONameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION));
229         String savedSslIssuedByUNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT));
230         int pinnedIpAddressesInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES));
231         String savedIpAddresses = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES));
232
233         // Initialize the saved SSL certificate date variables.
234         Date savedSslStartDate = null;
235         Date savedSslEndDate = null;
236
237         // Only get the saved SSL certificate dates from the cursor if they are not set to `0`.
238         if (domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) != 0) {
239             savedSslStartDate = new Date(domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)));
240         }
241
242         if (domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) != 0) {
243             savedSslEndDate = new Date(domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)));
244         }
245
246         // Create array adapters for the spinners.
247         ArrayAdapter<CharSequence> translatedUserAgentArrayAdapter = ArrayAdapter.createFromResource(context, R.array.translated_domain_settings_user_agent_names, R.layout.spinner_item);
248         ArrayAdapter<CharSequence> fontSizeArrayAdapter = ArrayAdapter.createFromResource(context, R.array.font_size_array, R.layout.spinner_item);
249         ArrayAdapter<CharSequence> swipeToRefreshArrayAdapter = ArrayAdapter.createFromResource(context, R.array.swipe_to_refresh_array, R.layout.spinner_item);
250         ArrayAdapter<CharSequence> nightModeArrayAdapter = ArrayAdapter.createFromResource(context, R.array.night_mode_array, R.layout.spinner_item);
251         ArrayAdapter<CharSequence> wideViewportArrayAdapter = ArrayAdapter.createFromResource(context, R.array.wide_viewport_array, R.layout.spinner_item);
252         ArrayAdapter<CharSequence> displayImagesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.display_webpage_images_array, R.layout.spinner_item);
253
254         // Set the drop down view resource on the spinners.
255         translatedUserAgentArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
256         fontSizeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
257         swipeToRefreshArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
258         nightModeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
259         wideViewportArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
260         displayImagesArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
261
262         // Set the array adapters for the spinners.
263         userAgentSpinner.setAdapter(translatedUserAgentArrayAdapter);
264         fontSizeSpinner.setAdapter(fontSizeArrayAdapter);
265         swipeToRefreshSpinner.setAdapter(swipeToRefreshArrayAdapter);
266         nightModeSpinner.setAdapter(nightModeArrayAdapter);
267         wideViewportSpinner.setAdapter(wideViewportArrayAdapter);
268         displayWebpageImagesSpinner.setAdapter(displayImagesArrayAdapter);
269
270         // Create a spannable string builder for each TextView that needs multiple colors of text.
271         SpannableStringBuilder savedSslIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString);
272         SpannableStringBuilder savedSslIssuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + savedSslIssuedToONameString);
273         SpannableStringBuilder savedSslIssuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + savedSslIssuedToUNameString);
274         SpannableStringBuilder savedSslIssuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslIssuedByCNameString);
275         SpannableStringBuilder savedSslIssuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + savedSslIssuedByONameString);
276         SpannableStringBuilder savedSslIssuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + savedSslIssuedByUNameString);
277
278         // Initialize the spannable string builders for the saved SSL certificate dates.
279         SpannableStringBuilder savedSslStartDateStringBuilder;
280         SpannableStringBuilder savedSslEndDateStringBuilder;
281
282         // Leave the SSL certificate dates empty if they are `null`.
283         if (savedSslStartDate == null) {
284             savedSslStartDateStringBuilder = new SpannableStringBuilder(startDateLabel);
285         } else {
286             savedSslStartDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslStartDate));
287         }
288
289         if (savedSslEndDate == null) {
290             savedSslEndDateStringBuilder = new SpannableStringBuilder(endDateLabel);
291         } else {
292             savedSslEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslEndDate));
293         }
294
295         // Create a red foreground color span.  The deprecated `resources.getColor` must be used until the minimum API >= 23.
296         final ForegroundColorSpan redColorSpan = new ForegroundColorSpan(resources.getColor(R.color.red_a700));
297
298         // Create a blue foreground color span.
299         final ForegroundColorSpan blueColorSpan;
300
301         // Set the blue color span according to the theme.  The deprecated `resources.getColor` must be used until the minimum API >= 23.
302         if (darkTheme) {
303             blueColorSpan = new ForegroundColorSpan(resources.getColor(R.color.blue_400));
304         } else {
305             blueColorSpan = new ForegroundColorSpan(resources.getColor(R.color.blue_700));
306         }
307
308         // Set the domain name from the the database cursor.
309         domainNameEditText.setText(domainNameString);
310
311         // Update the certificates' `Common Name` color when the domain name text changes.
312         domainNameEditText.addTextChangedListener(new TextWatcher() {
313             @Override
314             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
315                 // Do nothing.
316             }
317
318             @Override
319             public void onTextChanged(CharSequence s, int start, int before, int count) {
320                 // Do nothing.
321             }
322
323             @Override
324             public void afterTextChanged(Editable s) {
325                 // Get the new domain name.
326                 String newDomainName = domainNameEditText.getText().toString();
327
328                 // Check the saved SSL certificate against the new domain name.
329                 boolean savedSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, savedSslIssuedToCNameString);
330
331                 // Create a `SpannableStringBuilder` for the saved certificate `Common Name`.
332                 SpannableStringBuilder savedSslCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString);
333
334                 // Format the saved certificate `Common Name` color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
335                 if (savedSslMatchesNewDomainName) {
336                     savedSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
337                 } else {
338                     savedSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), savedSslCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
339                 }
340
341                 // Update the saved SSL issued to CName text view.
342                 savedSslIssuedToCNameTextView.setText(savedSslCNameStringBuilder);
343
344                 // Update the current website certificate if it exists.
345                 if (DomainsActivity.sslIssuedToCName != null) {
346                     // Check the current website certificate against the new domain name.
347                     boolean currentSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, DomainsActivity.sslIssuedToCName);
348
349                     // Create a `SpannableStringBuilder` for the current website certificate `Common Name`.
350                     SpannableStringBuilder currentSslCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName);
351
352                     // Format the current certificate `Common Name` color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
353                     if (currentSslMatchesNewDomainName) {
354                         currentSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
355                     } else {
356                         currentSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), currentSslCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
357                     }
358
359                     // Update the current SSL issued to CName text view.
360                     currentSslIssuedToCNameTextView.setText(currentSslCNameStringBuilder);
361                 }
362             }
363         });
364
365         // Create a boolean to track if night mode is enabled.
366         boolean nightModeEnabled = (nightModeInt == DomainsDatabaseHelper.ENABLED) || ((nightModeInt == DomainsDatabaseHelper.SYSTEM_DEFAULT) && defaultNightMode);
367
368         // Disable the JavaScript switch if night mode is enabled.
369         if (nightModeEnabled) {
370             javaScriptSwitch.setEnabled(false);
371         } else {
372             javaScriptSwitch.setEnabled(true);
373         }
374
375         // Set the JavaScript icon.
376         if ((javaScriptInt == 1) || nightModeEnabled) {
377             javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled));
378         } else {
379             javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode));
380         }
381
382         // Set the JavaScript switch status.
383         if (javaScriptInt == 1) {  // JavaScript is enabled.
384             javaScriptSwitch.setChecked(true);
385         } else {  // JavaScript is disabled.
386             javaScriptSwitch.setChecked(false);
387         }
388
389         // 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.
390         if (firstPartyCookiesInt == 1) {  // First-party cookies are enabled.
391             firstPartyCookiesSwitch.setChecked(true);
392             firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_enabled));
393         } else {  // First-party cookies are disabled.
394             firstPartyCookiesSwitch.setChecked(false);
395
396             // Set the icon according to the theme.
397             if (darkTheme) {
398                 firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
399             } else {
400                 firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
401             }
402         }
403
404         // Only display third-party cookies if SDK_INT >= 21.
405         if (Build.VERSION.SDK_INT >= 21) {  // Third-party cookies can be configured for API >= 21.
406             // Only enable third-party-cookies if first-party cookies are enabled.
407             if (firstPartyCookiesInt == 1) {  // First-party cookies are enabled.
408                 // 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.
409                 if (thirdPartyCookiesInt == 1) {  // Both first-party and third-party cookies are enabled.
410                     thirdPartyCookiesSwitch.setChecked(true);
411                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
412                 } else {  // First party cookies are enabled but third-party cookies are disabled.
413                     thirdPartyCookiesSwitch.setChecked(false);
414
415                     // Set the icon according to the theme.
416                     if (darkTheme) {
417                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
418                     } else {
419                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
420                     }
421                 }
422             } else {  // First-party cookies are disabled.
423                 // Set the status of third-party cookies.
424                 if (thirdPartyCookiesInt == 1) {
425                     thirdPartyCookiesSwitch.setChecked(true);
426                 } else {
427                     thirdPartyCookiesSwitch.setChecked(false);
428                 }
429
430                 // Disable the third-party cookies switch.
431                 thirdPartyCookiesSwitch.setEnabled(false);
432
433                 // Set the icon according to the theme.
434                 if (darkTheme) {
435                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_dark));
436                 } else {
437                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_light));
438                 }
439             }
440         } else {  // Third-party cookies cannot be configured for API <= 21.
441             // Hide the LinearLayout for third-party cookies.
442             thirdPartyCookiesLinearLayout.setVisibility(View.GONE);
443         }
444
445         // Only enable DOM storage if JavaScript is enabled.
446         if ((javaScriptInt == 1) || nightModeEnabled) {  // JavaScript is enabled.
447             // Enable the DOM storage `Switch`.
448             domStorageSwitch.setEnabled(true);
449
450             // Set the DOM storage status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
451             if (domStorageInt == 1) {  // Both JavaScript and DOM storage are enabled.
452                 domStorageSwitch.setChecked(true);
453                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
454             } else {  // JavaScript is enabled but DOM storage is disabled.
455                 // Set the DOM storage switch to off.
456                 domStorageSwitch.setChecked(false);
457
458                 // Set the icon according to the theme.
459                 if (darkTheme) {
460                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
461                 } else {
462                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
463                 }
464             }
465         } else {  // JavaScript is disabled.
466             // Disable the DOM storage `Switch`.
467             domStorageSwitch.setEnabled(false);
468
469             // Set the checked status of DOM storage.
470             if (domStorageInt == 1) {  // DOM storage is enabled but JavaScript is disabled.
471                 domStorageSwitch.setChecked(true);
472             } else {  // Both JavaScript and DOM storage are disabled.
473                 domStorageSwitch.setChecked(false);
474             }
475
476             // Set the icon according to the theme.
477             if (darkTheme) {
478                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_dark));
479             } else {
480                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_light));
481             }
482         }
483
484         // Set the form data status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.  Form data can be removed once the minimum API >= 26.
485         if (Build.VERSION.SDK_INT >= 26) {  // Form data no longer applies to newer versions of Android.
486             // Hide the form data switch.
487             formDataSwitch.setVisibility(View.GONE);
488         } else {  // Form data should be displayed because this is an older version of Android.
489             if (formDataInt == 1) {  // Form data is on.
490                 formDataSwitch.setChecked(true);
491                 formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_enabled));
492             } else {  // Form data is off.
493                 // Turn the form data switch to off.
494                 formDataSwitch.setChecked(false);
495
496                 // Set the icon according to the theme.
497                 if (darkTheme) {
498                     formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_dark));
499                 } else {
500                     formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_light));
501                 }
502             }
503         }
504
505         // Set the EasyList status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
506         if (easyListInt == 1) {  // EasyList is on.
507             // Turn the switch on.
508             easyListSwitch.setChecked(true);
509
510             // Set the icon according to the theme.
511             if (darkTheme) {
512                 easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_dark));
513             } else {
514                 easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_light));
515             }
516         } else {  // EasyList is off.
517             // Turn the switch off.
518             easyListSwitch.setChecked(false);
519
520             // Set the icon according to the theme.
521             if (darkTheme) {
522                 easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_dark));
523             } else {
524                 easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_light));
525             }
526         }
527
528         // Set the EasyPrivacy status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
529         if (easyPrivacyInt == 1) {  // EasyPrivacy is on.
530             // Turn the switch on.
531             easyPrivacySwitch.setChecked(true);
532
533             // Set the icon according to the theme.
534             if (darkTheme) {
535                 easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_dark));
536             } else {
537                 easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_light));
538             }
539         } else {  // EasyPrivacy is off.
540             // Turn the switch off.
541             easyPrivacySwitch.setChecked(false);
542
543             // Set the icon according to the theme.
544             if (darkTheme) {
545                 easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_dark));
546             } else {
547                 easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_light));
548             }
549         }
550
551         // 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.
552         if (fanboysAnnoyanceListInt == 1) {  // Fanboy's Annoyance List is on.
553             // Turn the switch on.
554             fanboysAnnoyanceListSwitch.setChecked(true);
555
556             // Set the icon according to the theme.
557             if (darkTheme) {
558                 fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
559             } else {
560                 fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
561             }
562         } else {  // Fanboy's Annoyance List is off.
563             // Turn the switch off.
564             fanboysAnnoyanceListSwitch.setChecked(false);
565
566             // Set the icon according to the theme.
567             if (darkTheme) {
568                 fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
569             } else {
570                 fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
571             }
572         }
573
574         // Only enable Fanboy's Social Blocking List if Fanboy's Annoyance List is off.
575         if (fanboysAnnoyanceListInt == 0) {  // Fanboy's Annoyance List is on.
576             // 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.
577             if (fanboysSocialBlockingListInt == 1) {  // Fanboy's Social Blocking List is on.
578                 // Enable the switch and turn it on.
579                 fanboysSocialBlockingListSwitch.setEnabled(true);
580                 fanboysSocialBlockingListSwitch.setChecked(true);
581
582                 // Set the icon according to the theme.
583                 if (darkTheme) {
584                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
585                 } else {
586                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
587                 }
588             } else {  // Fanboy's Social Blocking List is off.
589                 // Enable the switch but turn it off.
590                 fanboysSocialBlockingListSwitch.setEnabled(true);
591                 fanboysSocialBlockingListSwitch.setChecked(false);
592
593                 // Set the icon according to the theme.
594                 if (darkTheme) {
595                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
596                 } else {
597                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
598                 }
599             }
600         } else {  // Fanboy's Annoyance List is on.
601             // 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.
602             if (fanboysSocialBlockingListInt == 1) {  // Fanboy's Social Blocking List is on.
603                 // Disable the switch but turn it on.
604                 fanboysSocialBlockingListSwitch.setEnabled(false);
605                 fanboysSocialBlockingListSwitch.setChecked(true);
606             } else {  // Fanboy's Social Blocking List is off.
607                 // Disable the switch and turn it off.
608                 fanboysSocialBlockingListSwitch.setEnabled(false);
609                 fanboysSocialBlockingListSwitch.setChecked(false);
610             }
611
612             // Set the icon according to the theme.
613             if (darkTheme) {
614                 fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_dark));
615             } else {
616                 fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_light));
617             }
618         }
619
620         // Set the UltraList status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
621         if (ultraListInt == 1) {  // UltraList is on.
622             // Turn the switch on.
623             ultraListSwitch.setChecked(true);
624
625             // Set the icon according to the theme.
626             if (darkTheme) {
627                 ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_dark));
628             } else {
629                 ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_light));
630             }
631         } else {  // UltraList is off.
632             // Turn the switch off.
633             ultraListSwitch.setChecked(false);
634
635             // Set the icon according to the theme.
636             if (darkTheme) {
637                 ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_dark));
638             } else {
639                 ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_light));
640             }
641         }
642
643         // Set the UltraPrivacy status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
644         if (ultraPrivacyInt == 1) {  // UltraPrivacy is on.
645             // Turn the switch on.
646             ultraPrivacySwitch.setChecked(true);
647
648             // Set the icon according to the theme.
649             if (darkTheme) {
650                 ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_dark));
651             } else {
652                 ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_light));
653             }
654         } else {  // EasyPrivacy is off.
655             // Turn the switch off.
656             ultraPrivacySwitch.setChecked(false);
657
658             // Set the icon according to the theme.
659             if (darkTheme) {
660                 ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_dark));
661             } else {
662                 ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_light));
663             }
664         }
665
666         // Set the third-party resource blocking status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
667         if (blockAllThirdPartyRequestsInt == 1) {  // Blocking all third-party requests is on.
668             // Turn the switch on.
669             blockAllThirdPartyRequestsSwitch.setChecked(true);
670
671             // Set the icon according to the theme.
672             if (darkTheme) {
673                 blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_enabled_dark));
674             } else {
675                 blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_enabled_light));
676             }
677         } else {  // Blocking all third-party requests is off.
678             // Turn the switch off.
679             blockAllThirdPartyRequestsSwitch.setChecked(false);
680
681             // Set the icon according to the theme.
682             if (darkTheme) {
683                 blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_disabled_dark));
684             } else {
685                 blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_disabled_light));
686             }
687         }
688
689         // Inflated a WebView to get the default user agent.
690         // `@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.
691         @SuppressLint("InflateParams") View bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false);
692         WebView bareWebView = bareWebViewLayout.findViewById(R.id.bare_webview);
693         final String webViewDefaultUserAgentString = bareWebView.getSettings().getUserAgentString();
694
695         // Get a handle for the user agent array adapter.  This array does not contain the `System default` entry.
696         ArrayAdapter<CharSequence> userAgentNamesArray = ArrayAdapter.createFromResource(context, R.array.user_agent_names, R.layout.spinner_item);
697
698         // Get the positions of the user agent and the default user agent.
699         int userAgentArrayPosition = userAgentNamesArray.getPosition(currentUserAgentName);
700         int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
701
702         // Get a handle for the user agent data array.  This array does not contain the `System default` entry.
703         String[] userAgentDataArray = resources.getStringArray(R.array.user_agent_data);
704
705         // Set the user agent text.
706         if (currentUserAgentName.equals(getString(R.string.system_default_user_agent))) {  // Use the system default user agent.
707             // Set the user agent according to the system default.
708             switch (defaultUserAgentArrayPosition) {
709                 case MainWebViewActivity.UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
710                     // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
711                     userAgentTextView.setText(defaultUserAgentName);
712                     break;
713
714                 case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
715                     // Display the `WebView` default user agent.
716                     userAgentTextView.setText(webViewDefaultUserAgentString);
717                     break;
718
719                 case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
720                     // Display the custom user agent.
721                     userAgentTextView.setText(defaultCustomUserAgentString);
722                     break;
723
724                 default:
725                     // Get the user agent string from the user agent data array.
726                     userAgentTextView.setText(userAgentDataArray[defaultUserAgentArrayPosition]);
727             }
728         } else if (userAgentArrayPosition == MainWebViewActivity.UNRECOGNIZED_USER_AGENT) {  // A custom user agent is stored in the current user agent name.
729             // Set the user agent spinner to `Custom user agent`.
730             userAgentSpinner.setSelection(MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT);
731
732             // Hide the user agent TextView.
733             userAgentTextView.setVisibility(View.GONE);
734
735             // Show the custom user agent EditText and set the current user agent name as the text.
736             customUserAgentEditText.setVisibility(View.VISIBLE);
737             customUserAgentEditText.setText(currentUserAgentName);
738         } else {  // The user agent name contains one of the canonical user agents.
739             // 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.
740             userAgentSpinner.setSelection(userAgentArrayPosition + 1);
741
742             // Show the user agent TextView.
743             userAgentTextView.setVisibility(View.VISIBLE);
744
745             // Hide the custom user agent EditText.
746             customUserAgentEditText.setVisibility(View.GONE);
747
748             // Set the user agent text.
749             if (userAgentArrayPosition == MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT) {  // The WebView default user agent is selected.
750                 // Display the WebView default user agent.
751                 userAgentTextView.setText(webViewDefaultUserAgentString);
752             } else {  // A user agent besides the default is selected.
753                 // 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.
754                 userAgentTextView.setText(userAgentDataArray[userAgentArrayPosition + 1]);
755             }
756         }
757
758         // Open the user agent spinner when the text view is clicked.
759         userAgentTextView.setOnClickListener((View v) -> {
760             // Open the user agent spinner.
761             userAgentSpinner.performClick();
762         });
763
764         // Display the font size settings.
765         if (fontSizeInt == 0) {  // `0` is the code for system default font size.
766             // Set the font size to the system default
767             fontSizeSpinner.setSelection(0);
768
769             // Show the default font size text view.
770             defaultFontSizeTextView.setVisibility(View.VISIBLE);
771
772             // Hide the custom font size edit text.
773             customFontSizeEditText.setVisibility(View.GONE);
774
775             // Set the default font size as the text of the custom font size edit text.  This way, if the user switches to custom it will already be populated.
776             customFontSizeEditText.setText(defaultFontSizeString);
777         } else {  // A custom font size is selected.
778             // Set the spinner to the custom font size.
779             fontSizeSpinner.setSelection(1);
780
781             // Hide the default font size text view.
782             defaultFontSizeTextView.setVisibility(View.GONE);
783
784             // Show the custom font size edit text.
785             customFontSizeEditText.setVisibility(View.GONE);
786
787             // Set the custom font size.
788             customFontSizeEditText.setText(String.valueOf(fontSizeInt));
789         }
790
791         // Initialize the default font size percentage string.
792         String defaultFontSizePercentageString = defaultFontSizeString + "%";
793
794         // Set the default font size text in the text view.
795         defaultFontSizeTextView.setText(defaultFontSizePercentageString);
796
797         // Open the font size spinner when the text view is clicked.
798         defaultFontSizeTextView.setOnClickListener((View v) -> {
799             // Open the user agent spinner.
800             fontSizeSpinner.performClick();
801         });
802
803         // Display the swipe to refresh selection in the spinner.
804         swipeToRefreshSpinner.setSelection(swipeToRefreshInt);
805
806         // Set the swipe to refresh text.
807         if (defaultSwipeToRefresh) {
808             swipeToRefreshTextView.setText(swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
809         } else {
810             swipeToRefreshTextView.setText(swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
811         }
812
813         // 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.
814         switch (swipeToRefreshInt) {
815             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
816                 if (defaultSwipeToRefresh) {  // Swipe to refresh is enabled by default.
817                     // Set the icon according to the theme.
818                     if (darkTheme) {
819                         swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_dark));
820                     } else {
821                         swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_light));
822                     }
823                 } else {  // Swipe to refresh is disabled by default
824                     // Set the icon according to the theme.
825                     if (darkTheme) {
826                         swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_dark));
827                     } else {
828                         swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_light));
829                     }
830                 }
831
832                 // Show the swipe to refresh TextView.
833                 swipeToRefreshTextView.setVisibility(View.VISIBLE);
834                 break;
835
836             case DomainsDatabaseHelper.ENABLED:
837                 // Set the icon according to the theme.
838                 if (darkTheme) {
839                     swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_dark));
840                 } else {
841                     swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_light));
842                 }
843
844                 // Hide the swipe to refresh TextView.`
845                 swipeToRefreshTextView.setVisibility(View.GONE);
846                 break;
847
848             case DomainsDatabaseHelper.DISABLED:
849                 // Set the icon according to the theme.
850                 if (darkTheme) {
851                     swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_dark));
852                 } else {
853                     swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_light));
854                 }
855
856                 // Hide the swipe to refresh TextView.
857                 swipeToRefreshTextView.setVisibility(View.GONE);
858         }
859
860         // Open the swipe to refresh spinner when the TextView is clicked.
861         swipeToRefreshTextView.setOnClickListener((View v) -> {
862             // Open the swipe to refresh spinner.
863             swipeToRefreshSpinner.performClick();
864         });
865
866         // Display the night mode in the spinner.
867         nightModeSpinner.setSelection(nightModeInt);
868
869         // Set the default night mode text.
870         if (defaultNightMode) {
871             nightModeTextView.setText(nightModeArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
872         } else {
873             nightModeTextView.setText(nightModeArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
874         }
875
876         // 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.
877         switch (nightModeInt) {
878             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
879                 if (defaultNightMode) {  // Night mode enabled by default.
880                     // Set the icon according to the theme.
881                     if (darkTheme) {
882                         nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark));
883                     } else {
884                         nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light));
885                     }
886                 } else {  // Night mode disabled by default.
887                     // Set the icon according to the theme.
888                     if (darkTheme) {
889                         nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark));
890                     } else {
891                         nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light));
892                     }
893                 }
894
895                 // Show night mode TextView.
896                 nightModeTextView.setVisibility(View.VISIBLE);
897                 break;
898
899             case DomainsDatabaseHelper.ENABLED:
900                 // Set the icon according to the theme.
901                 if (darkTheme) {
902                     nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark));
903                 } else {
904                     nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light));
905                 }
906
907                 // Hide the night mode TextView.
908                 nightModeTextView.setVisibility(View.GONE);
909                 break;
910
911             case DomainsDatabaseHelper.DISABLED:
912                 // Set the icon according to the theme.
913                 if (darkTheme) {
914                     nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark));
915                 } else {
916                     nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light));
917                 }
918
919                 // Hide the night mode TextView.
920                 nightModeTextView.setVisibility(View.GONE);
921                 break;
922         }
923
924         // Open the night mode spinner when the TextView is clicked.
925         nightModeTextView.setOnClickListener((View v) -> {
926             // Open the night mode spinner.
927             nightModeSpinner.performClick();
928         });
929
930         // Display the wide viewport in the spinner.
931         wideViewportSpinner.setSelection(wideViewportInt);
932
933         // Set the default wide viewport text.
934         if (defaultWideViewport) {
935             wideViewportTextView.setText(wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
936         } else {
937             wideViewportTextView.setText(wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
938         }
939
940         // Set the wide viewport icon and text view settings.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
941         switch (wideViewportInt) {
942             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
943                 if (defaultWideViewport) {  // Wide viewport enabled by default.
944                     // Set the icon according to the theme.
945                     if (darkTheme) {
946                         wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_dark));
947                     } else {
948                         wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_light));
949                     }
950                 } else {  // Wide viewport disabled by default.
951                     if (darkTheme) {
952                         wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_dark));
953                     } else {
954                         wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_light));
955                     }
956                 }
957
958                 // Show the wide viewport text view.
959                 wideViewportTextView.setVisibility(View.VISIBLE);
960                 break;
961
962             case DomainsDatabaseHelper.ENABLED:
963                 // Set the icon according to the theme.
964                 if (darkTheme) {
965                     wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_dark));
966                 } else {
967                     wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_light));
968                 }
969
970                 // Hide the wide viewport text view.
971                 wideViewportTextView.setVisibility(View.GONE);
972                 break;
973
974             case DomainsDatabaseHelper.DISABLED:
975                 // Set the icon according to the theme.
976                 if (darkTheme) {
977                     wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_dark));
978                 } else {
979                     wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_light));
980                 }
981
982                 // Hide the wide viewport text view.
983                 wideViewportTextView.setVisibility(View.GONE);
984                 break;
985         }
986
987         // Open the wide viewport spinner when the text view is clicked.
988         wideViewportTextView.setOnClickListener((View view) -> {
989             // Open the wide viewport spinner.
990             wideViewportSpinner.performClick();
991         });
992
993         // Display the website images mode in the spinner.
994         displayWebpageImagesSpinner.setSelection(displayImagesInt);
995
996         // Set the default display images text.
997         if (defaultDisplayWebpageImages) {
998             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
999         } else {
1000             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
1001         }
1002
1003         // Set the display website images icon and text view settings.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
1004         switch (displayImagesInt) {
1005             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1006                 if (defaultDisplayWebpageImages) {  // Display webpage images enabled by default.
1007                     // Set the icon according to the theme.
1008                     if (darkTheme) {
1009                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
1010                     } else {
1011                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
1012                     }
1013                 } else {  // Display webpage images disabled by default.
1014                     // Set the icon according to the theme.
1015                     if (darkTheme) {
1016                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
1017                     } else {
1018                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
1019                     }
1020                 }
1021
1022                 // Show the display images text view.
1023                 displayImagesTextView.setVisibility(View.VISIBLE);
1024                 break;
1025
1026             case DomainsDatabaseHelper.ENABLED:
1027                 // Set the icon according to the theme.
1028                 if (darkTheme) {
1029                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
1030                 } else {
1031                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
1032                 }
1033
1034                 // Hide the display images text view.
1035                 displayImagesTextView.setVisibility(View.GONE);
1036                 break;
1037
1038             case DomainsDatabaseHelper.DISABLED:
1039                 // Set the icon according to the theme.
1040                 if (darkTheme) {
1041                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
1042                 } else {
1043                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
1044                 }
1045
1046                 // Hide the display images text view.
1047                 displayImagesTextView.setVisibility(View.GONE);
1048                 break;
1049         }
1050
1051         // Open the display images spinner when the text view is clicked.
1052         displayImagesTextView.setOnClickListener((View view) -> {
1053             // Open the user agent spinner.
1054             displayWebpageImagesSpinner.performClick();
1055         });
1056         
1057         // Set the pinned SSL certificate icon.
1058         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.
1059             // Check the switch.
1060             pinnedSslCertificateSwitch.setChecked(true);
1061
1062             // Set the icon according to the theme.
1063             if (darkTheme) {
1064                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark));
1065             } else {
1066                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_light));
1067             }
1068         } else {  // Pinned SSL certificate is disabled.
1069             // Uncheck the switch.
1070             pinnedSslCertificateSwitch.setChecked(false);
1071
1072             // Set the icon according to the theme.
1073             if (darkTheme) {
1074                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark));
1075             } else {
1076                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_light));
1077             }
1078         }
1079
1080         // Store the current date.
1081         Date currentDate = Calendar.getInstance().getTime();
1082
1083         // Setup the string builders to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1084         savedSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1085         savedSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1086         savedSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1087         savedSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1088         savedSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1089
1090         // Check the certificate Common Name against the domain name.
1091         boolean savedSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslIssuedToCNameString);
1092
1093         // Format the issued to Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1094         if (savedSslCommonNameMatchesDomainName) {
1095             savedSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1096         } else {
1097             savedSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), savedSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1098         }
1099
1100         //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1101         if ((savedSslStartDate != null) && savedSslStartDate.after(currentDate)) {  // The certificate start date is in the future.
1102             savedSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), savedSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1103         } else {  // The certificate start date is in the past.
1104             savedSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), savedSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1105         }
1106
1107         // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1108         if ((savedSslEndDate != null) && savedSslEndDate.before(currentDate)) {  // The certificate end date is in the past.
1109             savedSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), savedSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1110         } else {  // The certificate end date is in the future.
1111             savedSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), savedSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1112         }
1113
1114         // Display the saved website SSL certificate strings.
1115         savedSslIssuedToCNameTextView.setText(savedSslIssuedToCNameStringBuilder);
1116         savedSslIssuedToONameTextView.setText(savedSslIssuedToONameStringBuilder);
1117         savedSslIssuedToUNameTextView.setText(savedSslIssuedToUNameStringBuilder);
1118         savedSslIssuedByCNameTextView.setText(savedSslIssuedByCNameStringBuilder);
1119         savedSslIssuedByONameTextView.setText(savedSslIssuedByONameStringBuilder);
1120         savedSslIssuedByUNameTextView.setText(savedSslIssuedByUNameStringBuilder);
1121         savedSslStartDateTextView.setText(savedSslStartDateStringBuilder);
1122         savedSslEndDateTextView.setText(savedSslEndDateStringBuilder);
1123
1124         // Populate the current website SSL certificate if there is one.
1125         if (DomainsActivity.sslIssuedToCName != null) {
1126             // Get dates from the raw long values.
1127             Date currentSslStartDate = new Date(DomainsActivity.sslStartDateLong);
1128             Date currentSslEndDate = new Date(DomainsActivity.sslEndDateLong);
1129
1130             // Create a spannable string builder for each text view that needs multiple colors of text.
1131             SpannableStringBuilder currentSslIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName);
1132             SpannableStringBuilder currentSslIssuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedToOName);
1133             SpannableStringBuilder currentSslIssuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedToUName);
1134             SpannableStringBuilder currentSslIssuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedByCName);
1135             SpannableStringBuilder currentSslIssuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedByOName);
1136             SpannableStringBuilder currentSslIssuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedByUName);
1137             SpannableStringBuilder currentSslStartDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
1138                     .format(currentSslStartDate));
1139             SpannableStringBuilder currentSslEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
1140                     .format(currentSslEndDate));
1141
1142             // Setup the string builders to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1143             currentSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentSslIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1144             currentSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentSslIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1145             currentSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1146             currentSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentSslIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1147             currentSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentSslIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1148
1149             // Check the certificate Common Name against the domain name.
1150             boolean currentSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, DomainsActivity.sslIssuedToCName);
1151
1152             // Format the issued to Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1153             if (currentSslCommonNameMatchesDomainName) {
1154                 currentSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1155             } else {
1156                 currentSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), currentSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1157             }
1158
1159             //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1160             if (currentSslStartDate.after(currentDate)) {  // The certificate start date is in the future.
1161                 currentSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), currentSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1162             } else {  // The certificate start date is in the past.
1163                 currentSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), currentSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1164             }
1165
1166             // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1167             if (currentSslEndDate.before(currentDate)) {  // The certificate end date is in the past.
1168                 currentSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), currentSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1169             } else {  // The certificate end date is in the future.
1170                 currentSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), currentSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1171             }
1172
1173             // Display the current website SSL certificate strings.
1174             currentSslIssuedToCNameTextView.setText(currentSslIssuedToCNameStringBuilder);
1175             currentSslIssuedToONameTextView.setText(currentSslIssuedToONameStringBuilder);
1176             currentSslIssuedToUNameTextView.setText(currentSslIssuedToUNameStringBuilder);
1177             currentSslIssuedByCNameTextView.setText(currentSslIssuedByCNameStringBuilder);
1178             currentSslIssuedByONameTextView.setText(currentSslIssuedByONameStringBuilder);
1179             currentSslIssuedByUNameTextView.setText(currentSslIssuedByUNameStringBuilder);
1180             currentSslStartDateTextView.setText(currentSslStartDateStringBuilder);
1181             currentSslEndDateTextView.setText(currentSslEndDateStringBuilder);
1182         }
1183
1184         // Set the initial display status of the SSL certificates card views.
1185         if (pinnedSslCertificateSwitch.isChecked()) {  // An SSL certificate is pinned.
1186             // Set the visibility of the saved SSL certificate.
1187             if (savedSslIssuedToCNameString == null) {
1188                 savedSslCardView.setVisibility(View.GONE);
1189             } else {
1190                 savedSslCardView.setVisibility(View.VISIBLE);
1191             }
1192
1193             // Set the visibility of the current website SSL certificate.
1194             if (DomainsActivity.sslIssuedToCName == null) {  // There is no current SSL certificate.
1195                 // Hide the SSL certificate.
1196                 currentSslCardView.setVisibility(View.GONE);
1197
1198                 // Show the instruction.
1199                 noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
1200             } else {  // There is a current SSL certificate.
1201                 // Show the SSL certificate.
1202                 currentSslCardView.setVisibility(View.VISIBLE);
1203
1204                 // Hide the instruction.
1205                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1206             }
1207
1208             // Set the status of the radio buttons and the card view backgrounds.
1209             if (savedSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
1210                 // Check the saved SSL certificate radio button.
1211                 savedSslCertificateRadioButton.setChecked(true);
1212
1213                 // Uncheck the current website SSL certificate radio button.
1214                 currentWebsiteCertificateRadioButton.setChecked(false);
1215
1216                 // Darken the background of the current website SSL certificate linear layout according to the theme.
1217                 if (darkTheme) {
1218                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1219                 } else {
1220                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1221                 }
1222             } else if (currentSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1223                 // Check the current website SSL certificate radio button.
1224                 currentWebsiteCertificateRadioButton.setChecked(true);
1225
1226                 // Uncheck the saved SSL certificate radio button.
1227                 savedSslCertificateRadioButton.setChecked(false);
1228             } else {  // Neither SSL certificate is visible.
1229                 // Uncheck both radio buttons.
1230                 savedSslCertificateRadioButton.setChecked(false);
1231                 currentWebsiteCertificateRadioButton.setChecked(false);
1232             }
1233         } else {  // An SSL certificate is not pinned.
1234             // Hide the SSl certificates and instructions.
1235             savedSslCardView.setVisibility(View.GONE);
1236             currentSslCardView.setVisibility(View.GONE);
1237             noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1238
1239             // Uncheck the radio buttons.
1240             savedSslCertificateRadioButton.setChecked(false);
1241             currentWebsiteCertificateRadioButton.setChecked(false);
1242         }
1243
1244         // Set the pinned IP addresses icon.
1245         if (pinnedIpAddressesInt == 1) {  // Pinned IP addresses is enabled.  Once the minimum API >= 21 a selector can be sued as the tint mode instead of specifying different icons.
1246             // Check the switch.
1247             pinnedIpAddressesSwitch.setChecked(true);
1248
1249             // Set the icon according to the theme.
1250             if (darkTheme) {
1251                 pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark));
1252             } else {
1253                 pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_light));
1254             }
1255         } else {  // Pinned IP Addresses is disabled.
1256             // Uncheck the switch.
1257             pinnedIpAddressesSwitch.setChecked(false);
1258
1259             // Set the icon according to the theme.
1260             if (darkTheme) {
1261                 pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark));
1262             } else {
1263                 pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_light));
1264             }
1265         }
1266
1267         // Populate the saved and current IP addresses.
1268         savedIpAddressesTextView.setText(savedIpAddresses);
1269         currentIpAddressesTextView.setText(DomainsActivity.currentIpAddresses);
1270
1271         // Set the initial display status of the IP addresses card views.
1272         if (pinnedIpAddressesSwitch.isChecked()) {  // IP addresses are pinned.
1273             // Set the visibility of the saved IP addresses.
1274             if (savedIpAddresses == null) {  // There are no saved IP addresses.
1275                 savedIpAddressesCardView.setVisibility(View.GONE);
1276             } else {  // There are saved IP addresses.
1277                 savedIpAddressesCardView.setVisibility(View.VISIBLE);
1278             }
1279
1280             // Set the visibility of the current IP addresses.
1281             currentIpAddressesCardView.setVisibility(View.VISIBLE);
1282
1283             // Set the status of the radio buttons and the card view backgrounds.
1284             if (savedIpAddressesCardView.getVisibility() == View.VISIBLE) {  // The saved IP addresses are displayed.
1285                 // Check the saved IP addresses radio button.
1286                 savedIpAddressesRadioButton.setChecked(true);
1287
1288                 // Uncheck the current IP addresses radio button.
1289                 currentIpAddressesRadioButton.setChecked(false);
1290
1291                 // Darken the background of the current IP addresses linear layout according to the theme.
1292                 if (darkTheme) {
1293                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1294                 } else {
1295                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1296                 }
1297             } else {  // The saved IP addresses are hidden.
1298                 // Check the current IP addresses radio button.
1299                 currentIpAddressesRadioButton.setChecked(true);
1300
1301                 // Uncheck the saved IP addresses radio button.
1302                 savedIpAddressesRadioButton.setChecked(false);
1303             }
1304         } else {  // IP addresses are not pinned.
1305             // Hide the IP addresses card views.
1306             savedIpAddressesCardView.setVisibility(View.GONE);
1307             currentIpAddressesCardView.setVisibility(View.GONE);
1308
1309             // Uncheck the radio buttons.
1310             savedIpAddressesRadioButton.setChecked(false);
1311             currentIpAddressesRadioButton.setChecked(false);
1312         }
1313
1314
1315         // Set the JavaScript switch listener.
1316         javaScriptSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1317             if (isChecked) {  // JavaScript is enabled.
1318                 // Update the JavaScript icon.
1319                 javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled));
1320
1321                 // Enable the DOM storage `Switch`.
1322                 domStorageSwitch.setEnabled(true);
1323
1324                 // Update the DOM storage icon.
1325                 if (domStorageSwitch.isChecked()) {  // DOM storage is enabled.
1326                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
1327                 } else {  // DOM storage is disabled.
1328                     // Set the icon according to the theme.
1329                     if (darkTheme) {
1330                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
1331                     } else {
1332                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
1333                     }
1334                 }
1335             } else {  // JavaScript is disabled.
1336                 // Update the JavaScript icon.
1337                 javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode));
1338
1339                 // Disable the DOM storage `Switch`.
1340                 domStorageSwitch.setEnabled(false);
1341
1342                 // Set the DOM storage icon according to the theme.
1343                 if (darkTheme) {
1344                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_dark));
1345                 } else {
1346                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_light));
1347                 }
1348             }
1349         });
1350
1351         // Set the first-party cookies switch listener.
1352         firstPartyCookiesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1353             if (isChecked) {  // First-party cookies are enabled.
1354                 // Update the first-party cookies icon.
1355                 firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_enabled));
1356
1357                 // Enable the third-party cookies switch.
1358                 thirdPartyCookiesSwitch.setEnabled(true);
1359
1360                 // Update the third-party cookies icon.
1361                 if (thirdPartyCookiesSwitch.isChecked()) {  // Third-party cookies are enabled.
1362                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
1363                 } else {  // Third-party cookies are disabled.
1364                     // Set the third-party cookies icon according to the theme.
1365                     if (darkTheme) {
1366                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
1367                     } else {
1368                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
1369                     }
1370                 }
1371             } else {  // First-party cookies are disabled.
1372                 // Update the first-party cookies icon according to the theme.
1373                 if (darkTheme) {
1374                     firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
1375                 } else {
1376                     firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
1377                 }
1378
1379                 // Disable the third-party cookies switch.
1380                 thirdPartyCookiesSwitch.setEnabled(false);
1381
1382                 // Set the third-party cookies icon according to the theme.
1383                 if (darkTheme) {
1384                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_dark));
1385                 } else {
1386                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_light));
1387                 }
1388             }
1389         });
1390
1391         // Set the third-party cookies switch listener.
1392         thirdPartyCookiesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1393             // Update the icon.
1394             if (isChecked) {
1395                 thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
1396             } else {
1397                 // Update the third-party cookies icon according to the theme.
1398                 if (darkTheme) {
1399                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
1400                 } else {
1401                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
1402                 }
1403             }
1404         });
1405
1406         // Set the DOM Storage switch listener.
1407         domStorageSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1408             // Update the icon.
1409             if (isChecked) {
1410                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
1411             } else {
1412                 // Set the icon according to the theme.
1413                 if (darkTheme) {
1414                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
1415                 } else {
1416                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
1417                 }
1418             }
1419         });
1420
1421         // Set the form data switch listener.  It can be removed once the minimum API >= 26.
1422         if (Build.VERSION.SDK_INT < 26) {
1423             formDataSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1424                 // Update the icon.
1425                 if (isChecked) {
1426                     formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_enabled));
1427                 } else {
1428                     // Set the icon according to the theme.
1429                     if (darkTheme) {
1430                         formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_dark));
1431                     } else {
1432                         formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_light));
1433                     }
1434                 }
1435             });
1436         }
1437
1438         // Set the EasyList switch listener.
1439         easyListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1440             // Update the icon.
1441             if (isChecked) {  // EasyList is on.
1442                 // Set the icon according to the theme.
1443                 if (darkTheme) {
1444                     easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_dark));
1445                 } else {
1446                     easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_light));
1447                 }
1448             } else {  // EasyList is off.
1449                 // Set the icon according to the theme.
1450                 if (darkTheme) {
1451                     easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_dark));
1452                 } else {
1453                     easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_light));
1454                 }
1455             }
1456         });
1457
1458         // Set the EasyPrivacy switch listener.
1459         easyPrivacySwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1460             // Update the icon.
1461             if (isChecked) {  // EasyPrivacy is on.
1462                 // Set the icon according to the theme.
1463                 if (darkTheme) {
1464                     easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_dark));
1465                 } else {
1466                     easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_light));
1467                 }
1468             } else {  // EasyPrivacy is off.
1469                 // Set the icon according to the theme.
1470                 if (darkTheme) {
1471                     easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_dark));
1472                 } else {
1473                     easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_light));
1474                 }
1475             }
1476         });
1477
1478         // Set the Fanboy's Annoyance List switch listener.
1479         fanboysAnnoyanceListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1480             // Update the icon and Fanboy's Social Blocking List.
1481             if (isChecked) {  // Fanboy's Annoyance List is on.
1482                 // Set the icon according to the theme.
1483                 if (darkTheme) {
1484                     fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
1485                 } else {
1486                     fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
1487                 }
1488
1489                 // Disable the Fanboy's Social Blocking List switch.
1490                 fanboysSocialBlockingListSwitch.setEnabled(false);
1491
1492                 // Update the Fanboy's Social Blocking List icon according to the theme.
1493                 if (darkTheme) {
1494                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_dark));
1495                 } else {
1496                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_light));
1497                 }
1498             } else {  // Fanboy's Annoyance List is off.
1499                 // Set the icon according to the theme.
1500                 if (darkTheme) {
1501                     fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
1502                 } else {
1503                     fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
1504                 }
1505
1506                 // Enable the Fanboy's Social Blocking List switch.
1507                 fanboysSocialBlockingListSwitch.setEnabled(true);
1508
1509                 // Update the Fanboy's Social Blocking List icon.
1510                 if (fanboysSocialBlockingListSwitch.isChecked()) {  // Fanboy's Social Blocking List is on.
1511                     // Update the icon according to the theme.
1512                     if (darkTheme) {
1513                         fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
1514                     } else {
1515                         fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
1516                     }
1517                 } else {  // Fanboy's Social Blocking List is off.
1518                     // Update the icon according to the theme.
1519                     if (darkTheme) {
1520                         fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
1521                     } else {
1522                         fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
1523                     }
1524                 }
1525             }
1526
1527         });
1528
1529         // Set the Fanboy's Social Blocking List switch listener.
1530         fanboysSocialBlockingListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1531             // Update the icon.
1532             if (isChecked) {  // Fanboy's Social Blocking List is on.
1533                 // Set the icon according to the theme.
1534                 if (darkTheme) {
1535                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_dark));
1536                 } else {
1537                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_light));
1538                 }
1539             } else {  // Fanboy's Social Blocking List is off.
1540                 // Set the icon according to the theme.
1541                 if (darkTheme) {
1542                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_dark));
1543                 } else {
1544                     fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_light));
1545                 }
1546             }
1547         });
1548
1549         // Set the UltraList switch listener.
1550         ultraListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1551             // Update the icon.
1552             if (isChecked) {  // UltraList is on.
1553                 // Set the icon according to the theme.
1554                 if (darkTheme) {
1555                     ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_dark));
1556                 } else {
1557                     ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_light));
1558                 }
1559             } else {  // UltraList is off.
1560                 // Set the icon according to the theme.
1561                 if (darkTheme) {
1562                     ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_dark));
1563                 } else {
1564                     ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_light));
1565                 }
1566             }
1567         });
1568
1569         // Set the UltraPrivacy switch listener.
1570         ultraPrivacySwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1571             // Update the icon.
1572             if (isChecked) {  // UltraPrivacy is on.
1573                 // Set the icon according to the theme.
1574                 if (darkTheme) {
1575                     ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_dark));
1576                 } else {
1577                     ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_light));
1578                 }
1579             } else {  // UltraPrivacy is off.
1580                 // Set the icon according to the theme.
1581                 if (darkTheme) {
1582                     ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_dark));
1583                 } else {
1584                     ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_light));
1585                 }
1586             }
1587         });
1588
1589         // Set the block all third-party requests switch listener.
1590         blockAllThirdPartyRequestsSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1591             // Update the icon.
1592             if (isChecked) {  // Blocking all third-party requests is on.
1593                 // Set the icon according to the theme.
1594                 if (darkTheme) {
1595                     blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_enabled_dark));
1596                 } else {
1597                     blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_enabled_light));
1598                 }
1599             } else {  // Blocking all third-party requests is off.
1600                 // Set the icon according to the theme.
1601                 if (darkTheme) {
1602                     blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_disabled_dark));
1603                 } else {
1604                     blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_disabled_light));
1605                 }
1606             }
1607         });
1608
1609         // Set the user agent spinner listener.
1610         userAgentSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1611             @Override
1612             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1613                 // Set the new user agent.
1614                 switch (position) {
1615                     case MainWebViewActivity.DOMAINS_SYSTEM_DEFAULT_USER_AGENT:
1616                         // Show the user agent TextView.
1617                         userAgentTextView.setVisibility(View.VISIBLE);
1618
1619                         // Hide the custom user agent EditText.
1620                         customUserAgentEditText.setVisibility(View.GONE);
1621
1622                         // Set the user text.
1623                         switch (defaultUserAgentArrayPosition) {
1624                             case MainWebViewActivity.UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
1625                                 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
1626                                 userAgentTextView.setText(defaultUserAgentName);
1627                                 break;
1628
1629                             case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
1630                                 // Display the `WebView` default user agent.
1631                                 userAgentTextView.setText(webViewDefaultUserAgentString);
1632                                 break;
1633
1634                             case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
1635                                 // Display the custom user agent.
1636                                 userAgentTextView.setText(defaultCustomUserAgentString);
1637                                 break;
1638
1639                             default:
1640                                 // Get the user agent string from the user agent data array.
1641                                 userAgentTextView.setText(userAgentDataArray[defaultUserAgentArrayPosition]);
1642                         }
1643                         break;
1644
1645                     case MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT:
1646                         // Show the user agent TextView and set the text.
1647                         userAgentTextView.setVisibility(View.VISIBLE);
1648                         userAgentTextView.setText(webViewDefaultUserAgentString);
1649
1650                         // Hide the custom user agent EditTex.
1651                         customUserAgentEditText.setVisibility(View.GONE);
1652                         break;
1653
1654                     case MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT:
1655                         // Hide the user agent TextView.
1656                         userAgentTextView.setVisibility(View.GONE);
1657
1658                         // Show the custom user agent EditText and set the current user agent name as the text.
1659                         customUserAgentEditText.setVisibility(View.VISIBLE);
1660                         customUserAgentEditText.setText(currentUserAgentName);
1661                         break;
1662
1663                     default:
1664                         // 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.
1665                         userAgentTextView.setVisibility(View.VISIBLE);
1666                         userAgentTextView.setText(userAgentDataArray[position - 1]);
1667
1668                         // Hide `customUserAgentEditText`.
1669                         customUserAgentEditText.setVisibility(View.GONE);
1670                 }
1671             }
1672
1673             @Override
1674             public void onNothingSelected(AdapterView<?> parent) {
1675                 // Do nothing.
1676             }
1677         });
1678
1679         // Set the font size spinner listener.
1680         fontSizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1681             @Override
1682             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1683                 // Update the font size display options.
1684                 if (position == 0) {  // The system default font size has been selected.
1685                     // Show the default font size text view.
1686                     defaultFontSizeTextView.setVisibility(View.VISIBLE);
1687
1688                     // Hide the custom font size edit text.
1689                     customFontSizeEditText.setVisibility(View.GONE);
1690                 } else {  // A custom font size has been selected.
1691                     // Hide the default font size text view.
1692                     defaultFontSizeTextView.setVisibility(View.GONE);
1693
1694                     // Show the custom font size edit text.
1695                     customFontSizeEditText.setVisibility(View.VISIBLE);
1696                 }
1697             }
1698
1699             @Override
1700             public void onNothingSelected(AdapterView<?> parent) {
1701                 // Do nothing.
1702             }
1703         });
1704
1705         // Set the swipe to refresh spinner listener.
1706         swipeToRefreshSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1707             @Override
1708             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1709                 // 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.
1710                 switch (position) {
1711                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1712                         if (defaultSwipeToRefresh) {  // Swipe to refresh enabled by default.
1713                             // Set the icon according to the theme.
1714                             if (darkTheme) {
1715                                 swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_dark));
1716                             } else {
1717                                 swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_light));
1718                             }
1719                         } else {  // Swipe to refresh disabled by default.
1720                             // Set the icon according to the theme.
1721                             if (darkTheme) {
1722                                 swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_dark));
1723                             } else {
1724                                 swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_light));
1725                             }
1726                         }
1727
1728                         // Show the swipe to refresh TextView.
1729                         swipeToRefreshTextView.setVisibility(View.VISIBLE);
1730                         break;
1731
1732                     case DomainsDatabaseHelper.ENABLED:
1733                         // Set the icon according to the theme.
1734                         if (darkTheme) {
1735                             swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_dark));
1736                         } else {
1737                             swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_light));
1738                         }
1739
1740                         // Hide the swipe to refresh TextView.
1741                         swipeToRefreshTextView.setVisibility(View.GONE);
1742                         break;
1743
1744                     case DomainsDatabaseHelper.DISABLED:
1745                         // Set the icon according to the theme.
1746                         if (darkTheme) {
1747                             swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_dark));
1748                         } else {
1749                             swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_light));
1750                         }
1751
1752                         // Hide the swipe to refresh TextView.
1753                         swipeToRefreshTextView.setVisibility(View.GONE);
1754                 }
1755             }
1756
1757             @Override
1758             public void onNothingSelected(AdapterView<?> parent) {
1759                 // Do nothing.
1760             }
1761         });
1762
1763         // Set the night mode spinner listener.
1764         nightModeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1765             @Override
1766             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1767                 // 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.
1768                 switch (position) {
1769                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1770                         if (defaultNightMode) {  // Night mode enabled by default.
1771                             // Set the icon according to the theme.
1772                             if (darkTheme) {
1773                                 nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark));
1774                             } else {
1775                                 nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light));
1776                             }
1777                         } else {  // Night mode disabled by default.
1778                             // Set the icon according to the theme.
1779                             if (darkTheme) {
1780                                 nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark));
1781                             } else {
1782                                 nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light));
1783                             }
1784                         }
1785
1786                         // Show the night mode TextView.
1787                         nightModeTextView.setVisibility(View.VISIBLE);
1788                         break;
1789
1790                     case DomainsDatabaseHelper.ENABLED:
1791                         // Set the icon according to the theme.
1792                         if (darkTheme) {
1793                             nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark));
1794                         } else {
1795                             nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light));
1796                         }
1797
1798                         // Hide `nightModeTextView`.
1799                         nightModeTextView.setVisibility(View.GONE);
1800                         break;
1801
1802                     case DomainsDatabaseHelper.DISABLED:
1803                         // Set the icon according to the theme.
1804                         if (darkTheme) {
1805                             nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark));
1806                         } else {
1807                             nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light));
1808                         }
1809
1810                         // Hide `nightModeTextView`.
1811                         nightModeTextView.setVisibility(View.GONE);
1812                         break;
1813                 }
1814
1815                 // Create a boolean to store the current night mode setting.
1816                 boolean currentNightModeEnabled = (position == DomainsDatabaseHelper.ENABLED) || ((position == DomainsDatabaseHelper.SYSTEM_DEFAULT) && defaultNightMode);
1817
1818                 // Disable the JavaScript switch if night mode is enabled.
1819                 if (currentNightModeEnabled) {
1820                     javaScriptSwitch.setEnabled(false);
1821                 } else {
1822                     javaScriptSwitch.setEnabled(true);
1823                 }
1824
1825                 // Update the JavaScript icon.
1826                 if ((javaScriptInt == 1) || currentNightModeEnabled) {
1827                     javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled));
1828                 } else {
1829                     javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode));
1830                 }
1831
1832                 // Update the DOM storage status.
1833                 if ((javaScriptInt == 1) || currentNightModeEnabled) {  // JavaScript is enabled.
1834                     // Enable the DOM storage `Switch`.
1835                     domStorageSwitch.setEnabled(true);
1836
1837                     // Set the DOM storage status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
1838                     if (domStorageInt == 1) {  // Both JavaScript and DOM storage are enabled.
1839                         domStorageSwitch.setChecked(true);
1840                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
1841                     } else {  // JavaScript is enabled but DOM storage is disabled.
1842                         // Set the DOM storage switch to off.
1843                         domStorageSwitch.setChecked(false);
1844
1845                         // Set the icon according to the theme.
1846                         if (darkTheme) {
1847                             domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
1848                         } else {
1849                             domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
1850                         }
1851                     }
1852                 } else {  // JavaScript is disabled.
1853                     // Disable the DOM storage `Switch`.
1854                     domStorageSwitch.setEnabled(false);
1855
1856                     // Set the checked status of DOM storage.
1857                     if (domStorageInt == 1) {  // DOM storage is enabled but JavaScript is disabled.
1858                         domStorageSwitch.setChecked(true);
1859                     } else {  // Both JavaScript and DOM storage are disabled.
1860                         domStorageSwitch.setChecked(false);
1861                     }
1862
1863                     // Set the icon according to the theme.
1864                     if (darkTheme) {
1865                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_dark));
1866                     } else {
1867                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_light));
1868                     }
1869                 }
1870             }
1871
1872             @Override
1873             public void onNothingSelected(AdapterView<?> parent) {
1874                 // Do nothing.
1875             }
1876         });
1877
1878         // Set the wide viewport spinner listener.
1879         wideViewportSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1880             @Override
1881             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1882                 // Update the icon and the visibility of the wide viewport text view.
1883                 switch (position) {
1884                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1885                         if (defaultWideViewport) {  // Wide viewport is enabled by default.
1886                             // Set the icon according to the theme.
1887                             if (darkTheme) {
1888                                 wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_dark));
1889                             } else {
1890                                 wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_light));
1891                             }
1892                         } else {  // Wide viewport is disabled by default.
1893                             if (darkTheme) {
1894                                 wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_dark));
1895                             } else {
1896                                 wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_light));
1897                             }
1898                         }
1899
1900                         // Show the wide viewport text view.
1901                         wideViewportTextView.setVisibility(View.VISIBLE);
1902                         break;
1903
1904                     case DomainsDatabaseHelper.ENABLED:
1905                         // Set the icon according to the theme.
1906                         if (darkTheme) {
1907                             wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_dark));
1908                         } else {
1909                             wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_light));
1910                         }
1911
1912                         // Hide the wide viewport text view.
1913                         wideViewportTextView.setVisibility(View.GONE);
1914                         break;
1915
1916                     case DomainsDatabaseHelper.DISABLED:
1917                         // Set the icon according to the theme.
1918                         if (darkTheme) {
1919                             wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_dark));
1920                         } else {
1921                             wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_light));
1922                         }
1923
1924                         // Hid ethe wide viewport text view.
1925                         wideViewportTextView.setVisibility(View.GONE);
1926                         break;
1927                 }
1928             }
1929
1930             @Override
1931             public void onNothingSelected(AdapterView<?> parent) {
1932                 // Do nothing.
1933             }
1934         });
1935
1936         // Set the display webpage images spinner listener.
1937         displayWebpageImagesSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1938             @Override
1939             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1940                 // Update the icon and the visibility of the display images text view.
1941                 switch (position) {
1942                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1943                         if (defaultDisplayWebpageImages) {  // Display webpage images is enabled by default.
1944                             // Set the icon according to the theme.
1945                             if (darkTheme) {
1946                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
1947                             } else {
1948                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
1949                             }
1950                         } else {  // Display webpage images is disabled by default.
1951                             // Set the icon according to the theme.
1952                             if (darkTheme) {
1953                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
1954                             } else {
1955                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
1956                             }
1957                         }
1958
1959                         // Show the display images text view.
1960                         displayImagesTextView.setVisibility(View.VISIBLE);
1961                         break;
1962
1963                     case DomainsDatabaseHelper.ENABLED:
1964                         // Set the icon according to the theme.
1965                         if (darkTheme) {
1966                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
1967                         } else {
1968                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
1969                         }
1970
1971                         // Hide the display images text view.
1972                         displayImagesTextView.setVisibility(View.GONE);
1973                         break;
1974
1975                     case DomainsDatabaseHelper.DISABLED:
1976                         // Set the icon according to the theme.
1977                         if (darkTheme) {
1978                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
1979                         } else {
1980                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
1981                         }
1982
1983                         // Hide the display images text view.
1984                         displayImagesTextView.setVisibility(View.GONE);
1985                         break;
1986                 }
1987             }
1988
1989             @Override
1990             public void onNothingSelected(AdapterView<?> parent) {
1991                 // Do nothing.
1992             }
1993         });
1994         
1995         // Set the pinned SSL certificate switch listener.
1996         pinnedSslCertificateSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1997             // Update the icon.
1998             if (isChecked) {  // SSL certificate pinning is enabled.
1999                 // Set the icon according to the theme.
2000                 if (darkTheme) {
2001                     pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark));
2002                 } else {
2003                     pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_light));
2004                 }
2005
2006                 // Update the visibility of the saved SSL certificate.
2007                 if (savedSslIssuedToCNameString == null) {
2008                     savedSslCardView.setVisibility(View.GONE);
2009                 } else {
2010                     savedSslCardView.setVisibility(View.VISIBLE);
2011                 }
2012
2013                 // Update the visibility of the current website SSL certificate.
2014                 if (DomainsActivity.sslIssuedToCName == null) {
2015                     // Hide the SSL certificate.
2016                     currentSslCardView.setVisibility(View.GONE);
2017
2018                     // Show the instruction.
2019                     noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
2020                 } else {
2021                     // Show the SSL certificate.
2022                     currentSslCardView.setVisibility(View.VISIBLE);
2023
2024                     // Hide the instruction.
2025                     noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
2026                 }
2027
2028                 // Set the status of the radio buttons.
2029                 if (savedSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
2030                     // Check the saved SSL certificate radio button.
2031                     savedSslCertificateRadioButton.setChecked(true);
2032
2033                     // Uncheck the current website SSL certificate radio button.
2034                     currentWebsiteCertificateRadioButton.setChecked(false);
2035
2036                     // Set the background of the saved SSL certificate linear layout to be transparent.
2037                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
2038
2039                     // Darken the background of the current website SSL certificate linear layout according to the theme.
2040                     if (darkTheme) {
2041                         currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2042                     } else {
2043                         currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2044                     }
2045
2046                     // Scroll to the current website SSL certificate card.
2047                     savedSslCardView.getParent().requestChildFocus(savedSslCardView, savedSslCardView);
2048                 } else if (currentSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
2049                     // Check the current website SSL certificate radio button.
2050                     currentWebsiteCertificateRadioButton.setChecked(true);
2051
2052                     // Uncheck the saved SSL certificate radio button.
2053                     savedSslCertificateRadioButton.setChecked(false);
2054
2055                     // Set the background of the current website SSL certificate linear layout to be transparent.
2056                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
2057
2058                     // Darken the background of the saved SSL certificate linear layout according to the theme.
2059                     if (darkTheme) {
2060                         savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2061                     } else {
2062                         savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2063                     }
2064
2065                     // Scroll to the current website SSL certificate card.
2066                     currentSslCardView.getParent().requestChildFocus(currentSslCardView, currentSslCardView);
2067                 } else {  // Neither SSL certificate is visible.
2068                     // Uncheck both radio buttons.
2069                     savedSslCertificateRadioButton.setChecked(false);
2070                     currentWebsiteCertificateRadioButton.setChecked(false);
2071
2072                     // Scroll to the current website SSL certificate card.
2073                     noCurrentWebsiteCertificateTextView.getParent().requestChildFocus(noCurrentWebsiteCertificateTextView, noCurrentWebsiteCertificateTextView);
2074                 }
2075             } else {  // SSL certificate pinning is disabled.
2076                 // Set the icon according to the theme.
2077                 if (darkTheme) {
2078                     pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark));
2079                 } else {
2080                     pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_light));
2081                 }
2082
2083                 // Hide the SSl certificates and instructions.
2084                 savedSslCardView.setVisibility(View.GONE);
2085                 currentSslCardView.setVisibility(View.GONE);
2086                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
2087
2088                 // Uncheck the radio buttons.
2089                 savedSslCertificateRadioButton.setChecked(false);
2090                 currentWebsiteCertificateRadioButton.setChecked(false);
2091             }
2092         });
2093
2094         savedSslCardView.setOnClickListener((View view) -> {
2095             // Check the saved SSL certificate radio button.
2096             savedSslCertificateRadioButton.setChecked(true);
2097
2098             // Uncheck the current website SSL certificate radio button.
2099             currentWebsiteCertificateRadioButton.setChecked(false);
2100
2101             // Set the background of the saved SSL certificate linear layout to be transparent.
2102             savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
2103
2104             // Darken the background of the current website SSL certificate linear layout according to the theme.
2105             if (darkTheme) {
2106                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2107             } else {
2108                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2109             }
2110         });
2111
2112         savedSslCertificateRadioButton.setOnClickListener((View view) -> {
2113             // Check the saved SSL certificate radio button.
2114             savedSslCertificateRadioButton.setChecked(true);
2115
2116             // Uncheck the current website SSL certificate radio button.
2117             currentWebsiteCertificateRadioButton.setChecked(false);
2118
2119             // Set the background of the saved SSL certificate linear layout to be transparent.
2120             savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
2121
2122             // Darken the background of the current website SSL certificate linear layout according to the theme.
2123             if (darkTheme) {
2124                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2125             } else {
2126                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2127             }
2128         });
2129
2130         currentSslCardView.setOnClickListener((View view) -> {
2131             // Check the current website SSL certificate radio button.
2132             currentWebsiteCertificateRadioButton.setChecked(true);
2133
2134             // Uncheck the saved SSL certificate radio button.
2135             savedSslCertificateRadioButton.setChecked(false);
2136
2137             // Set the background of the current website SSL certificate linear layout to be transparent.
2138             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
2139
2140             // Darken the background of the saved SSL certificate linear layout according to the theme.
2141             if (darkTheme) {
2142                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2143             } else {
2144                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2145             }
2146         });
2147
2148         currentWebsiteCertificateRadioButton.setOnClickListener((View view) -> {
2149             // Check the current website SSL certificate radio button.
2150             currentWebsiteCertificateRadioButton.setChecked(true);
2151
2152             // Uncheck the saved SSL certificate radio button.
2153             savedSslCertificateRadioButton.setChecked(false);
2154
2155             // Set the background of the current website SSL certificate linear layout to be transparent.
2156             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
2157
2158             // Darken the background of the saved SSL certificate linear layout according to the theme.
2159             if (darkTheme) {
2160                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2161             } else {
2162                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2163             }
2164         });
2165
2166         // Set the pinned IP addresses switch listener.
2167         pinnedIpAddressesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
2168             // Update the icon.
2169             if (isChecked) {  // IP addresses pinning is enabled.
2170                 // Set the icon according to the theme.
2171                 if (darkTheme) {
2172                     pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark));
2173                 } else {
2174                     pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_light));
2175                 }
2176
2177                 // Update the visibility of the saved IP addresses card view.
2178                 if (savedIpAddresses == null) {  // There are no saved IP addresses.
2179                     savedIpAddressesCardView.setVisibility(View.GONE);
2180                 } else {  // There are saved IP addresses.
2181                     savedIpAddressesCardView.setVisibility(View.VISIBLE);
2182                 }
2183
2184                 // Show the current IP addresses card view.
2185                 currentIpAddressesCardView.setVisibility(View.VISIBLE);
2186
2187                 // Set the status of the radio buttons.
2188                 if (savedIpAddressesCardView.getVisibility() == View.VISIBLE) {  // The saved IP addresses are visible.
2189                     // Check the saved IP addresses radio button.
2190                     savedIpAddressesRadioButton.setChecked(true);
2191
2192                     // Uncheck the current IP addresses radio button.
2193                     currentIpAddressesRadioButton.setChecked(false);
2194
2195                     // Set the background of the saved IP addresses linear layout to be transparent.
2196                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
2197
2198                     // Darken the background of the current IP addresses linear layout according to the theme.
2199                     if (darkTheme) {
2200                         currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2201                     } else {
2202                         currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2203                     }
2204                 } else {  // The saved IP addresses are not visible.
2205                     // Check the current IP addresses radio button.
2206                     currentIpAddressesRadioButton.setChecked(true);
2207
2208                     // Uncheck the saved IP addresses radio button.
2209                     savedIpAddressesRadioButton.setChecked(false);
2210
2211                     // Set the background of the current IP addresses linear layout to be transparent.
2212                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2213
2214                     // Darken the background of the saved IP addresses linear layout according to the theme.
2215                     if (darkTheme) {
2216                         savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2217                     } else {
2218                         savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2219                     }
2220                 }
2221
2222                 // Scroll to the bottom of the card views.
2223                 currentIpAddressesCardView.getParent().requestChildFocus(currentIpAddressesCardView, currentIpAddressesCardView);
2224             } else {  // IP addresses pinning is disabled.
2225                 // Set the icon according to the theme.
2226                 if (darkTheme) {
2227                     pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark));
2228                 } else {
2229                     pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_light));
2230                 }
2231
2232                 // Hide the IP addresses card views.
2233                 savedIpAddressesCardView.setVisibility(View.GONE);
2234                 currentIpAddressesCardView.setVisibility(View.GONE);
2235
2236                 // Uncheck the radio buttons.
2237                 savedIpAddressesRadioButton.setChecked(false);
2238                 currentIpAddressesRadioButton.setChecked(false);
2239             }
2240         });
2241
2242         savedIpAddressesCardView.setOnClickListener((View view) -> {
2243             // Check the saved IP addresses radio button.
2244             savedIpAddressesRadioButton.setChecked(true);
2245
2246             // Uncheck the current website IP addresses radio button.
2247             currentIpAddressesRadioButton.setChecked(false);
2248
2249             // Set the background of the saved IP addresses linear layout to be transparent.
2250             savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2251
2252             // Darken the background of the current IP addresses linear layout according to the theme.
2253             if (darkTheme) {
2254                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2255             } else {
2256                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2257             }
2258         });
2259
2260         savedIpAddressesRadioButton.setOnClickListener((View view) -> {
2261             // Check the saved IP addresses radio button.
2262             savedIpAddressesRadioButton.setChecked(true);
2263
2264             // Uncheck the current website IP addresses radio button.
2265             currentIpAddressesRadioButton.setChecked(false);
2266
2267             // Set the background of the saved IP addresses linear layout to be transparent.
2268             savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2269
2270             // Darken the background of the current IP addresses linear layout according to the theme.
2271             if (darkTheme) {
2272                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2273             } else {
2274                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2275             }
2276         });
2277
2278         currentIpAddressesCardView.setOnClickListener((View view) -> {
2279             // Check the current IP addresses radio button.
2280             currentIpAddressesRadioButton.setChecked(true);
2281
2282             // Uncheck the saved IP addresses radio button.
2283             savedIpAddressesRadioButton.setChecked(false);
2284
2285             // Set the background of the current IP addresses linear layout to be transparent.
2286             currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2287
2288             // Darken the background of the saved IP addresses linear layout according to the theme.
2289             if (darkTheme) {
2290                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2291             } else {
2292                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2293             }
2294         });
2295
2296         currentIpAddressesRadioButton.setOnClickListener((View view) -> {
2297             // Check the current IP addresses radio button.
2298             currentIpAddressesRadioButton.setChecked(true);
2299
2300             // Uncheck the saved IP addresses radio button.
2301             savedIpAddressesRadioButton.setChecked(false);
2302
2303             // Set the background of the current IP addresses linear layout to be transparent.
2304             currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2305
2306             // Darken the background of the saved IP addresses linear layout according to the theme.
2307             if (darkTheme) {
2308                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2309             } else {
2310                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2311             }
2312         });
2313
2314         return domainSettingsView;
2315     }
2316
2317     private boolean checkDomainNameAgainstCertificate(String domainName, String certificateCommonName) {
2318         // Initialize `domainNamesMatch`.
2319         boolean domainNamesMatch = false;
2320
2321         // Check various wildcard permutations if `domainName` and `certificateCommonName` are not empty.
2322         // `noinspection ConstantCondition` removes Android Studio's incorrect lint warning that `domainName` can never be `null`.
2323         if ((domainName != null) && (certificateCommonName != null)) {
2324             // Check if the domains match.
2325             if (domainName.equals(certificateCommonName)) {
2326                 domainNamesMatch = true;
2327             }
2328
2329             // If `domainName` starts with a wildcard, check the base domain against all the subdomains of `certificateCommonName`.
2330             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2)) {
2331                 // Remove the initial `*.`.
2332                 String baseDomainName = domainName.substring(2);
2333
2334                 // Setup a copy of `certificateCommonName` to test subdomains.
2335                 String certificateCommonNameSubdomain = certificateCommonName;
2336
2337                 // Check all the subdomains in `certificateCommonNameSubdomain` against `baseDomainName`.
2338                 while (!domainNamesMatch && certificateCommonNameSubdomain.contains(".")) {  // Stop checking if we know that `domainNamesMatch` is `true` or if we run out of  `.`.
2339                     // Test the `certificateCommonNameSubdomain` against `baseDomainName`.
2340                     if (certificateCommonNameSubdomain.equals(baseDomainName)) {
2341                         domainNamesMatch = true;
2342                     }
2343
2344                     // Strip out the lowest subdomain of `certificateCommonNameSubdomain`.
2345                     try {
2346                         certificateCommonNameSubdomain = certificateCommonNameSubdomain.substring(certificateCommonNameSubdomain.indexOf(".") + 1);
2347                     } catch (IndexOutOfBoundsException e) {  // `certificateCommonNameSubdomain` ends with `.`.
2348                         certificateCommonNameSubdomain = "";
2349                     }
2350                 }
2351             }
2352
2353             // If `certificateCommonName` starts with a wildcard, check the base common name against all the subdomains of `domainName`.
2354             if (!domainNamesMatch && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
2355                 // Remove the initial `*.`.
2356                 String baseCertificateCommonName = certificateCommonName.substring(2);
2357
2358                 // Setup a copy of `domainName` to test subdomains.
2359                 String domainNameSubdomain = domainName;
2360
2361                 // Check all the subdomains in `domainNameSubdomain` against `baseCertificateCommonName`.
2362                 while (!domainNamesMatch && domainNameSubdomain.contains(".") && (domainNameSubdomain.length() > 2)) {
2363                     // Test the `domainNameSubdomain` against `baseCertificateCommonName`.
2364                     if (domainNameSubdomain.equals(baseCertificateCommonName)) {
2365                         domainNamesMatch = true;
2366                     }
2367
2368                     // Strip out the lowest subdomain of `domainNameSubdomain`.
2369                     try {
2370                         domainNameSubdomain = domainNameSubdomain.substring(domainNameSubdomain.indexOf(".") + 1);
2371                     } catch (IndexOutOfBoundsException e) { // `domainNameSubdomain` ends with `.`.
2372                         domainNameSubdomain = "";
2373                     }
2374                 }
2375             }
2376
2377             // If both names start with a wildcard, check if the root of one contains the root of the other.
2378             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2) && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
2379                 // Remove the wildcards.
2380                 String rootDomainName = domainName.substring(2);
2381                 String rootCertificateCommonName = certificateCommonName.substring(2);
2382
2383                 // Check if one name ends with the contents of the other.  If so, there will be overlap in the their wildcard subdomains.
2384                 if (rootDomainName.endsWith(rootCertificateCommonName) || rootCertificateCommonName.endsWith(rootDomainName)) {
2385                     domainNamesMatch = true;
2386                 }
2387             }
2388         }
2389
2390         return domainNamesMatch;
2391     }
2392 }