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