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