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