Migrate five dialogs to Kotlin. https://redmine.stoutner.com/issues/604
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / 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 redColorSpan;
308         final ForegroundColorSpan blueColorSpan;
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_YES) {
312             redColorSpan = new ForegroundColorSpan(resources.getColor(R.color.red_900));
313             blueColorSpan = new ForegroundColorSpan(resources.getColor(R.color.violet_500));
314         } else {
315             redColorSpan = new ForegroundColorSpan(resources.getColor(R.color.red_a700));
316             blueColorSpan = new ForegroundColorSpan(resources.getColor(R.color.blue_700));
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                 if (thirdPartyCookiesInt == 1) {
425                     thirdPartyCookiesSwitch.setChecked(true);
426                 } else {
427                     thirdPartyCookiesSwitch.setChecked(false);
428                 }
429
430                 // Disable the third-party cookies switch.
431                 thirdPartyCookiesSwitch.setEnabled(false);
432
433                 // Set the icon according to the theme.
434                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
435                     thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_ghosted_night, null));
436                 } else {
437                     thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_ghosted_day, null));
438                 }
439             }
440         } else {  // Third-party cookies cannot be configured for API <= 21.
441             // Hide the LinearLayout for third-party cookies.
442             thirdPartyCookiesLinearLayout.setVisibility(View.GONE);
443         }
444
445         // Only enable DOM storage if JavaScript is enabled.
446         if (javaScriptInt == 1) {  // JavaScript is enabled.
447             // Enable the DOM storage `Switch`.
448             domStorageSwitch.setEnabled(true);
449
450             // Set the DOM storage status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
451             if (domStorageInt == 1) {  // Both JavaScript and DOM storage are enabled.
452                 domStorageSwitch.setChecked(true);
453                 domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_enabled, null));
454             } else {  // JavaScript is enabled but DOM storage is disabled.
455                 // Set the DOM storage switch to off.
456                 domStorageSwitch.setChecked(false);
457
458                 // Set the icon according to the theme.
459                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
460                     domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled_night, null));
461                 } else {
462                     domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled_day, null));
463                 }
464             }
465         } else {  // JavaScript is disabled.
466             // Disable the DOM storage `Switch`.
467             domStorageSwitch.setEnabled(false);
468
469             // Set the checked status of DOM storage.
470             if (domStorageInt == 1) {  // DOM storage is enabled but JavaScript is disabled.
471                 domStorageSwitch.setChecked(true);
472             } else {  // Both JavaScript and DOM storage are disabled.
473                 domStorageSwitch.setChecked(false);
474             }
475
476             // Set the icon according to the theme.
477             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
478                 domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_ghosted_night, null));
479             } else {
480                 domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_ghosted_day, null));
481             }
482         }
483
484         // Set the form data visibility.  Form data can be removed once the minimum API >= 26.
485         if (Build.VERSION.SDK_INT >= 26) {  // Form data no longer applies to newer versions of Android.
486             // Hide the form data image view and switch.
487             formDataImageView.setVisibility(View.GONE);
488             formDataSwitch.setVisibility(View.GONE);
489         } else {  // Form data should be displayed because this is an older version of Android.
490             if (formDataInt == 1) {  // Form data is on.
491                 // Turn the form data switch on.
492                 formDataSwitch.setChecked(true);
493
494                 // Set the form data icon.
495                 formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_enabled, null));
496             } else {  // Form data is off.
497                 // Turn the form data switch to off.
498                 formDataSwitch.setChecked(false);
499
500                 // 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.
501                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
502                     formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_disabled_night, null));
503                 } else {
504                     formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_disabled_day, null));
505                 }
506             }
507         }
508
509         // Set the EasyList status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
510         if (easyListInt == 1) {  // EasyList is on.
511             // Turn the switch on.
512             easyListSwitch.setChecked(true);
513
514             // Set the icon according to the theme.
515             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
516                 easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_night, null));
517             } else {
518                 easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_day, null));
519             }
520         } else {  // EasyList is off.
521             // Turn the switch off.
522             easyListSwitch.setChecked(false);
523
524             // Set the icon according to the theme.
525             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
526                 easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_night, null));
527             } else {
528                 easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_day, null));
529             }
530         }
531
532         // Set the EasyPrivacy status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
533         if (easyPrivacyInt == 1) {  // EasyPrivacy is on.
534             // Turn the switch on.
535             easyPrivacySwitch.setChecked(true);
536
537             // Set the icon according to the theme.
538             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
539                 easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_night, null));
540             } else {
541                 easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_day, null));
542             }
543         } else {  // EasyPrivacy is off.
544             // Turn the switch off.
545             easyPrivacySwitch.setChecked(false);
546
547             // Set the icon according to the theme.
548             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
549                 easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_night, null));
550             } else {
551                 easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_day, null));
552             }
553         }
554
555         // 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.
556         if (fanboysAnnoyanceListInt == 1) {  // Fanboy's Annoyance List is on.
557             // Turn the switch on.
558             fanboysAnnoyanceListSwitch.setChecked(true);
559
560             // Set the icon according to the theme.
561             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
562                 fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_night, null));
563             } else {
564                 fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_day, null));
565             }
566         } else {  // Fanboy's Annoyance List is off.
567             // Turn the switch off.
568             fanboysAnnoyanceListSwitch.setChecked(false);
569
570             // Set the icon according to the theme.
571             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
572                 fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_night, null));
573             } else {
574                 fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_day, null));
575             }
576         }
577
578         // Only enable Fanboy's Social Blocking List if Fanboy's Annoyance List is off.
579         if (fanboysAnnoyanceListInt == 0) {  // Fanboy's Annoyance List is on.
580             // Enable Fanboy's Social Blocking List switch.
581             fanboysSocialBlockingListSwitch.setEnabled(true);
582
583             // 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.
584             if (fanboysSocialBlockingListInt == 1) {  // Fanboy's Social Blocking List is on.
585                 // Turn on Fanboy's Social Blocking List switch.
586                 fanboysSocialBlockingListSwitch.setChecked(true);
587
588                 // Set the icon according to the theme.
589                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
590                     fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_night, null));
591                 } else {
592                     fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_day, null));
593                 }
594             } else {  // Fanboy's Social Blocking List is off.
595                 // Turn off Fanboy's Social Blocking List switch.
596                 fanboysSocialBlockingListSwitch.setChecked(false);
597
598                 // Set the icon according to the theme.
599                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
600                     fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_night, null));
601                 } else {
602                     fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_day, null));
603                 }
604             }
605         } else {  // Fanboy's Annoyance List is on.
606             // Disable Fanboy's Social Blocking List switch.
607             fanboysSocialBlockingListSwitch.setEnabled(false);
608
609             // Handle the status of Fanboy's Social Blocking List.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
610             if (fanboysSocialBlockingListInt == 1) {  // Fanboy's Social Blocking List is on.
611                 // Turn on Fanboy's Social Blocking List switch.
612                 fanboysSocialBlockingListSwitch.setChecked(true);
613             } else {  // Fanboy's Social Blocking List is off.
614                 // Turn off Fanboy's Social Blocking List switch.
615                 fanboysSocialBlockingListSwitch.setChecked(false);
616             }
617
618             // Set the icon according to the theme.
619             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
620                 fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_ghosted_night, null));
621             } else {
622                 fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_ghosted_day, null));
623             }
624         }
625
626         // Set the UltraList status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
627         if (ultraListInt == 1) {  // UltraList is on.
628             // Turn the switch on.
629             ultraListSwitch.setChecked(true);
630
631             // Set the icon according to the theme.
632             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
633                 ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_night, null));
634             } else {
635                 ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_day, null));
636             }
637         } else {  // UltraList is off.
638             // Turn the switch off.
639             ultraListSwitch.setChecked(false);
640
641             // Set the icon according to the theme.
642             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
643                 ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_night, null));
644             } else {
645                 ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_day, null));
646             }
647         }
648
649         // Set the UltraPrivacy status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
650         if (ultraPrivacyInt == 1) {  // UltraPrivacy is on.
651             // Turn the switch on.
652             ultraPrivacySwitch.setChecked(true);
653
654             // Set the icon according to the theme.
655             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
656                 ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_night, null));
657             } else {
658                 ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_day, null));
659             }
660         } else {  // EasyPrivacy is off.
661             // Turn the switch off.
662             ultraPrivacySwitch.setChecked(false);
663
664             // Set the icon according to the theme.
665             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
666                 ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_night, null));
667             } else {
668                 ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_day, null));
669             }
670         }
671
672         // 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.
673         if (blockAllThirdPartyRequestsInt == 1) {  // Blocking all third-party requests is on.
674             // Turn the switch on.
675             blockAllThirdPartyRequestsSwitch.setChecked(true);
676
677             // Set the icon according to the theme.
678             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
679                 blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_enabled_night, null));
680             } else {
681                 blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_enabled_day, null));
682             }
683         } else {  // Blocking all third-party requests is off.
684             // Turn the switch off.
685             blockAllThirdPartyRequestsSwitch.setChecked(false);
686
687             // Set the icon according to the theme.
688             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
689                 blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_disabled_night, null));
690             } else {
691                 blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_disabled_day, null));
692             }
693         }
694
695         // Inflated a WebView to get the default user agent.
696         // `@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.
697         @SuppressLint("InflateParams") View bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false);
698         WebView bareWebView = bareWebViewLayout.findViewById(R.id.bare_webview);
699         final String webViewDefaultUserAgentString = bareWebView.getSettings().getUserAgentString();
700
701         // Get a handle for the user agent array adapter.  This array does not contain the `System default` entry.
702         ArrayAdapter<CharSequence> userAgentNamesArray = ArrayAdapter.createFromResource(context, R.array.user_agent_names, R.layout.spinner_item);
703
704         // Get the positions of the user agent and the default user agent.
705         int userAgentArrayPosition = userAgentNamesArray.getPosition(currentUserAgentName);
706         int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
707
708         // Get a handle for the user agent data array.  This array does not contain the `System default` entry.
709         String[] userAgentDataArray = resources.getStringArray(R.array.user_agent_data);
710
711         // Set the user agent text.
712         if (currentUserAgentName.equals(getString(R.string.system_default_user_agent))) {  // Use the system default user agent.
713             // Set the user agent according to the system default.
714             switch (defaultUserAgentArrayPosition) {
715                 case MainWebViewActivity.UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
716                     // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
717                     userAgentTextView.setText(defaultUserAgentName);
718                     break;
719
720                 case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
721                     // Display the `WebView` default user agent.
722                     userAgentTextView.setText(webViewDefaultUserAgentString);
723                     break;
724
725                 case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
726                     // Display the custom user agent.
727                     userAgentTextView.setText(defaultCustomUserAgentString);
728                     break;
729
730                 default:
731                     // Get the user agent string from the user agent data array.
732                     userAgentTextView.setText(userAgentDataArray[defaultUserAgentArrayPosition]);
733             }
734         } else if (userAgentArrayPosition == MainWebViewActivity.UNRECOGNIZED_USER_AGENT) {  // A custom user agent is stored in the current user agent name.
735             // Set the user agent spinner to `Custom user agent`.
736             userAgentSpinner.setSelection(MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT);
737
738             // Hide the user agent TextView.
739             userAgentTextView.setVisibility(View.GONE);
740
741             // Show the custom user agent EditText and set the current user agent name as the text.
742             customUserAgentEditText.setVisibility(View.VISIBLE);
743             customUserAgentEditText.setText(currentUserAgentName);
744         } else {  // The user agent name contains one of the canonical user agents.
745             // 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.
746             userAgentSpinner.setSelection(userAgentArrayPosition + 1);
747
748             // Show the user agent TextView.
749             userAgentTextView.setVisibility(View.VISIBLE);
750
751             // Hide the custom user agent EditText.
752             customUserAgentEditText.setVisibility(View.GONE);
753
754             // Set the user agent text.
755             if (userAgentArrayPosition == MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT) {  // The WebView default user agent is selected.
756                 // Display the WebView default user agent.
757                 userAgentTextView.setText(webViewDefaultUserAgentString);
758             } else {  // A user agent besides the default is selected.
759                 // 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.
760                 userAgentTextView.setText(userAgentDataArray[userAgentArrayPosition + 1]);
761             }
762         }
763
764         // Open the user agent spinner when the text view is clicked.
765         userAgentTextView.setOnClickListener((View v) -> {
766             // Open the user agent spinner.
767             userAgentSpinner.performClick();
768         });
769
770         // Display the font size settings.
771         if (fontSizeInt == 0) {  // `0` is the code for system default font size.
772             // Set the font size to the system default
773             fontSizeSpinner.setSelection(0);
774
775             // Show the default font size text view.
776             defaultFontSizeTextView.setVisibility(View.VISIBLE);
777
778             // Hide the custom font size edit text.
779             customFontSizeEditText.setVisibility(View.GONE);
780
781             // 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.
782             customFontSizeEditText.setText(defaultFontSizeString);
783         } else {  // A custom font size is selected.
784             // Set the spinner to the custom font size.
785             fontSizeSpinner.setSelection(1);
786
787             // Hide the default font size text view.
788             defaultFontSizeTextView.setVisibility(View.GONE);
789
790             // Show the custom font size edit text.
791             customFontSizeEditText.setVisibility(View.GONE);
792
793             // Set the custom font size.
794             customFontSizeEditText.setText(String.valueOf(fontSizeInt));
795         }
796
797         // Initialize the default font size percentage string.
798         String defaultFontSizePercentageString = defaultFontSizeString + "%";
799
800         // Set the default font size text in the text view.
801         defaultFontSizeTextView.setText(defaultFontSizePercentageString);
802
803         // Open the font size spinner when the text view is clicked.
804         defaultFontSizeTextView.setOnClickListener((View v) -> {
805             // Open the user agent spinner.
806             fontSizeSpinner.performClick();
807         });
808
809         // Select the swipe to refresh selection in the spinner.
810         swipeToRefreshSpinner.setSelection(swipeToRefreshInt);
811
812         // Set the swipe to refresh text.
813         if (defaultSwipeToRefresh) {
814             swipeToRefreshTextView.setText(swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
815         } else {
816             swipeToRefreshTextView.setText(swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
817         }
818
819         // 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.
820         switch (swipeToRefreshInt) {
821             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
822                 if (defaultSwipeToRefresh) {  // Swipe to refresh is enabled by default.
823                     // Set the icon according to the theme.
824                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
825                         swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_night, null));
826                     } else {
827                         swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_day, null));
828                     }
829                 } else {  // Swipe to refresh is disabled by default
830                     // Set the icon according to the theme.
831                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
832                         swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_night, null));
833                     } else {
834                         swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_day, null));
835                     }
836                 }
837
838                 // Show the swipe to refresh TextView.
839                 swipeToRefreshTextView.setVisibility(View.VISIBLE);
840                 break;
841
842             case DomainsDatabaseHelper.ENABLED:
843                 // Set the icon according to the theme.
844                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
845                     swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_night, null));
846                 } else {
847                     swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_day, null));
848                 }
849
850                 // Hide the swipe to refresh TextView.`
851                 swipeToRefreshTextView.setVisibility(View.GONE);
852                 break;
853
854             case DomainsDatabaseHelper.DISABLED:
855                 // Set the icon according to the theme.
856                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
857                     swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_night, null));
858                 } else {
859                     swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_day, null));
860                 }
861
862                 // Hide the swipe to refresh TextView.
863                 swipeToRefreshTextView.setVisibility(View.GONE);
864                 break;
865         }
866
867         // Open the swipe to refresh spinner when the TextView is clicked.
868         swipeToRefreshTextView.setOnClickListener((View v) -> {
869             // Open the swipe to refresh spinner.
870             swipeToRefreshSpinner.performClick();
871         });
872
873         // Get the WebView theme string arrays.
874         String[] webViewThemeStringArray = resources.getStringArray(R.array.webview_theme_array);
875         String[] webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values);
876
877         // Define an app WebView theme entry number.
878         int appWebViewThemeEntryNumber;
879
880         // 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.
881         if (defaultWebViewTheme.equals(webViewThemeEntryValuesStringArray[1])) {  // The light theme is selected.
882             // Store the default WebView theme entry number.
883             appWebViewThemeEntryNumber = 1;
884         } else if (defaultWebViewTheme.equals(webViewThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
885             // Store the default WebView theme entry number.
886             appWebViewThemeEntryNumber = 2;
887         } else {  // The system default theme is selected.
888             // Store the default WebView theme entry number.
889             appWebViewThemeEntryNumber = 0;
890         }
891
892         // Set the WebView theme visibility.
893         if (Build.VERSION.SDK_INT < 21) {  // The WebView theme cannot be set on API 19.
894             // Get a handle for the webView theme linear layout.
895             LinearLayout webViewThemeLinearLayout = domainSettingsView.findViewById(R.id.webview_theme_linearlayout);
896
897             // Hide the WebView theme linear layout.
898             webViewThemeLinearLayout.setVisibility(View.GONE);
899         } else {  // The WebView theme can be set on API >= 21.
900             // Select the WebView theme in the spinner.
901             webViewThemeSpinner.setSelection(webViewThemeInt);
902
903             // Set the WebView theme text.
904             if (appWebViewThemeEntryNumber == DomainsDatabaseHelper.SYSTEM_DEFAULT) {  // The app WebView theme is system default.
905                 // Set the text according to the current UI theme.
906                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
907                     webViewThemeTextView.setText(webViewThemeStringArray[DomainsDatabaseHelper.LIGHT_THEME]);
908                 } else {
909                     webViewThemeTextView.setText(webViewThemeStringArray[DomainsDatabaseHelper.DARK_THEME]);
910                 }
911             } else {  // The app WebView theme is not system default.
912                 // Set the text according to the app WebView theme.
913                 webViewThemeTextView.setText(webViewThemeStringArray[appWebViewThemeEntryNumber]);
914             }
915
916             // 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.
917             switch (webViewThemeInt) {
918                 case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // The domain WebView theme is system default.
919                     // Set the icon according to the app WebView theme.
920                     switch (appWebViewThemeEntryNumber) {
921                         case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // The default WebView theme is system default.
922                             // Set the icon according to the app theme.
923                             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
924                                 // Set the light mode icon.
925                                 webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_day, null));
926                             } else {
927                                 // Set the dark theme icon.
928                                 webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_night, null));
929                             }
930                             break;
931
932                         case DomainsDatabaseHelper.LIGHT_THEME:  // the default WebView theme is light.
933                             // Set the icon according to the app theme.
934                             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
935                                 webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_day, null));
936                             } else {
937                                 webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_night, null));
938                             }
939                             break;
940
941                         case DomainsDatabaseHelper.DARK_THEME:  // the default WebView theme is dark.
942                             // Set the icon according to the app theme.
943                             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
944                                 webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_day, null));
945                             } else {
946                                 webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_night, null));                            }
947                             break;
948                     }
949
950                     // Show the WebView theme text view.
951                     webViewThemeTextView.setVisibility(View.VISIBLE);
952                     break;
953
954                 case DomainsDatabaseHelper.LIGHT_THEME:  // The domain WebView theme is light.
955                     // Set the icon according to the app theme.
956                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
957                         webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_day, null));
958                     } else {
959                         webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_night, null));
960                     }
961
962                     // Hide the WebView theme text view.
963                     webViewThemeTextView.setVisibility(View.GONE);
964                     break;
965
966                 case DomainsDatabaseHelper.DARK_THEME:  // The domain WebView theme is dark.
967                     // Set the icon according to the app theme.
968                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
969                         webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_day, null));
970                     } else {
971                         webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_night, null));
972                     }
973
974                     // Hide the WebView theme text view.
975                     webViewThemeTextView.setVisibility(View.GONE);
976                     break;
977             }
978
979             // Open the WebView theme spinner when the text view is clicked.
980             webViewThemeTextView.setOnClickListener((View v) -> {
981                 // Open the WebView theme spinner.
982                 webViewThemeSpinner.performClick();
983             });
984         }
985
986         // Select the wide viewport in the spinner.
987         wideViewportSpinner.setSelection(wideViewportInt);
988
989         // Set the default wide viewport text.
990         if (defaultWideViewport) {
991             wideViewportTextView.setText(wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
992         } else {
993             wideViewportTextView.setText(wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
994         }
995
996         // 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.
997         switch (wideViewportInt) {
998             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
999                 if (defaultWideViewport) {  // Wide viewport enabled by default.
1000                     // Set the icon according to the theme.
1001                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1002                         wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_night, null));
1003                     } else {
1004                         wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_day, null));
1005                     }
1006                 } else {  // Wide viewport disabled by default.
1007                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1008                         wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_night, null));
1009                     } else {
1010                         wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_day, null));
1011                     }
1012                 }
1013
1014                 // Show the wide viewport text view.
1015                 wideViewportTextView.setVisibility(View.VISIBLE);
1016                 break;
1017
1018             case DomainsDatabaseHelper.ENABLED:
1019                 // Set the icon according to the theme.
1020                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1021                     wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_night, null));
1022                 } else {
1023                     wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_day, null));
1024                 }
1025
1026                 // Hide the wide viewport text view.
1027                 wideViewportTextView.setVisibility(View.GONE);
1028                 break;
1029
1030             case DomainsDatabaseHelper.DISABLED:
1031                 // Set the icon according to the theme.
1032                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1033                     wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_night, null));
1034                 } else {
1035                     wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_day, null));
1036                 }
1037
1038                 // Hide the wide viewport text view.
1039                 wideViewportTextView.setVisibility(View.GONE);
1040                 break;
1041         }
1042
1043         // Open the wide viewport spinner when the text view is clicked.
1044         wideViewportTextView.setOnClickListener((View view) -> {
1045             // Open the wide viewport spinner.
1046             wideViewportSpinner.performClick();
1047         });
1048
1049         // Display the website images mode in the spinner.
1050         displayWebpageImagesSpinner.setSelection(displayImagesInt);
1051
1052         // Set the default display images text.
1053         if (defaultDisplayWebpageImages) {
1054             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
1055         } else {
1056             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
1057         }
1058
1059         // 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.
1060         switch (displayImagesInt) {
1061             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1062                 if (defaultDisplayWebpageImages) {  // Display webpage images enabled by default.
1063                     // Set the icon according to the theme.
1064                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1065                         displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_night, null));
1066                     } else {
1067                         displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_day, null));
1068                     }
1069                 } else {  // Display webpage images disabled by default.
1070                     // Set the icon according to the theme.
1071                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1072                         displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_night, null));
1073                     } else {
1074                         displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_day, null));
1075                     }
1076                 }
1077
1078                 // Show the display images text view.
1079                 displayImagesTextView.setVisibility(View.VISIBLE);
1080                 break;
1081
1082             case DomainsDatabaseHelper.ENABLED:
1083                 // Set the icon according to the theme.
1084                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1085                     displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_night, null));
1086                 } else {
1087                     displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_day, null));
1088                 }
1089
1090                 // Hide the display images text view.
1091                 displayImagesTextView.setVisibility(View.GONE);
1092                 break;
1093
1094             case DomainsDatabaseHelper.DISABLED:
1095                 // Set the icon according to the theme.
1096                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1097                     displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_night, null));
1098                 } else {
1099                     displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_day, null));
1100                 }
1101
1102                 // Hide the display images text view.
1103                 displayImagesTextView.setVisibility(View.GONE);
1104                 break;
1105         }
1106
1107         // Open the display images spinner when the text view is clicked.
1108         displayImagesTextView.setOnClickListener((View view) -> {
1109             // Open the user agent spinner.
1110             displayWebpageImagesSpinner.performClick();
1111         });
1112         
1113         // Set the pinned SSL certificate icon.
1114         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.
1115             // Check the switch.
1116             pinnedSslCertificateSwitch.setChecked(true);
1117
1118             // Set the icon according to the theme.
1119             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1120                 pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_night, null));
1121             } else {
1122                 pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_day, null));
1123             }
1124         } else {  // Pinned SSL certificate is disabled.
1125             // Uncheck the switch.
1126             pinnedSslCertificateSwitch.setChecked(false);
1127
1128             // Set the icon according to the theme.
1129             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1130                 pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_night, null));
1131             } else {
1132                 pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_day, null));
1133             }
1134         }
1135
1136         // Store the current date.
1137         Date currentDate = Calendar.getInstance().getTime();
1138
1139         // Setup the string builders to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1140         savedSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1141         savedSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1142         savedSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1143         savedSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1144         savedSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1145
1146         // Check the certificate Common Name against the domain name.
1147         boolean savedSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslIssuedToCNameString);
1148
1149         // Format the issued to Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1150         if (savedSslCommonNameMatchesDomainName) {
1151             savedSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1152         } else {
1153             savedSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), savedSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1154         }
1155
1156         //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1157         if ((savedSslStartDate != null) && savedSslStartDate.after(currentDate)) {  // The certificate start date is in the future.
1158             savedSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), savedSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1159         } else {  // The certificate start date is in the past.
1160             savedSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), savedSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1161         }
1162
1163         // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1164         if ((savedSslEndDate != null) && savedSslEndDate.before(currentDate)) {  // The certificate end date is in the past.
1165             savedSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), savedSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1166         } else {  // The certificate end date is in the future.
1167             savedSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), savedSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1168         }
1169
1170         // Display the saved website SSL certificate strings.
1171         savedSslIssuedToCNameTextView.setText(savedSslIssuedToCNameStringBuilder);
1172         savedSslIssuedToONameTextView.setText(savedSslIssuedToONameStringBuilder);
1173         savedSslIssuedToUNameTextView.setText(savedSslIssuedToUNameStringBuilder);
1174         savedSslIssuedByCNameTextView.setText(savedSslIssuedByCNameStringBuilder);
1175         savedSslIssuedByONameTextView.setText(savedSslIssuedByONameStringBuilder);
1176         savedSslIssuedByUNameTextView.setText(savedSslIssuedByUNameStringBuilder);
1177         savedSslStartDateTextView.setText(savedSslStartDateStringBuilder);
1178         savedSslEndDateTextView.setText(savedSslEndDateStringBuilder);
1179
1180         // Populate the current website SSL certificate if there is one.
1181         if (DomainsActivity.sslIssuedToCName != null) {
1182             // Get dates from the raw long values.
1183             Date currentSslStartDate = new Date(DomainsActivity.sslStartDateLong);
1184             Date currentSslEndDate = new Date(DomainsActivity.sslEndDateLong);
1185
1186             // Create a spannable string builder for each text view that needs multiple colors of text.
1187             SpannableStringBuilder currentSslIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName);
1188             SpannableStringBuilder currentSslIssuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedToOName);
1189             SpannableStringBuilder currentSslIssuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedToUName);
1190             SpannableStringBuilder currentSslIssuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedByCName);
1191             SpannableStringBuilder currentSslIssuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedByOName);
1192             SpannableStringBuilder currentSslIssuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedByUName);
1193             SpannableStringBuilder currentSslStartDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
1194                     .format(currentSslStartDate));
1195             SpannableStringBuilder currentSslEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
1196                     .format(currentSslEndDate));
1197
1198             // Setup the string builders to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1199             currentSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentSslIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1200             currentSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentSslIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1201             currentSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1202             currentSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentSslIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1203             currentSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentSslIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1204
1205             // Check the certificate Common Name against the domain name.
1206             boolean currentSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, DomainsActivity.sslIssuedToCName);
1207
1208             // Format the issued to Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1209             if (currentSslCommonNameMatchesDomainName) {
1210                 currentSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1211             } else {
1212                 currentSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), currentSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1213             }
1214
1215             //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1216             if (currentSslStartDate.after(currentDate)) {  // The certificate start date is in the future.
1217                 currentSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), currentSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1218             } else {  // The certificate start date is in the past.
1219                 currentSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), currentSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1220             }
1221
1222             // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1223             if (currentSslEndDate.before(currentDate)) {  // The certificate end date is in the past.
1224                 currentSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), currentSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1225             } else {  // The certificate end date is in the future.
1226                 currentSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), currentSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1227             }
1228
1229             // Display the current website SSL certificate strings.
1230             currentSslIssuedToCNameTextView.setText(currentSslIssuedToCNameStringBuilder);
1231             currentSslIssuedToONameTextView.setText(currentSslIssuedToONameStringBuilder);
1232             currentSslIssuedToUNameTextView.setText(currentSslIssuedToUNameStringBuilder);
1233             currentSslIssuedByCNameTextView.setText(currentSslIssuedByCNameStringBuilder);
1234             currentSslIssuedByONameTextView.setText(currentSslIssuedByONameStringBuilder);
1235             currentSslIssuedByUNameTextView.setText(currentSslIssuedByUNameStringBuilder);
1236             currentSslStartDateTextView.setText(currentSslStartDateStringBuilder);
1237             currentSslEndDateTextView.setText(currentSslEndDateStringBuilder);
1238         }
1239
1240         // Set the initial display status of the SSL certificates card views.
1241         if (pinnedSslCertificateSwitch.isChecked()) {  // An SSL certificate is pinned.
1242             // Set the visibility of the saved SSL certificate.
1243             if (savedSslIssuedToCNameString == null) {
1244                 savedSslCardView.setVisibility(View.GONE);
1245             } else {
1246                 savedSslCardView.setVisibility(View.VISIBLE);
1247             }
1248
1249             // Set the visibility of the current website SSL certificate.
1250             if (DomainsActivity.sslIssuedToCName == null) {  // There is no current SSL certificate.
1251                 // Hide the SSL certificate.
1252                 currentSslCardView.setVisibility(View.GONE);
1253
1254                 // Show the instruction.
1255                 noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
1256             } else {  // There is a current SSL certificate.
1257                 // Show the SSL certificate.
1258                 currentSslCardView.setVisibility(View.VISIBLE);
1259
1260                 // Hide the instruction.
1261                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1262             }
1263
1264             // Set the status of the radio buttons and the card view backgrounds.
1265             if (savedSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
1266                 // Check the saved SSL certificate radio button.
1267                 savedSslCertificateRadioButton.setChecked(true);
1268
1269                 // Uncheck the current website SSL certificate radio button.
1270                 currentWebsiteCertificateRadioButton.setChecked(false);
1271
1272                 // Darken the background of the current website SSL certificate linear layout according to the theme.
1273                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1274                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1275                 } else {
1276                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1277                 }
1278             } else if (currentSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1279                 // Check the current website SSL certificate radio button.
1280                 currentWebsiteCertificateRadioButton.setChecked(true);
1281
1282                 // Uncheck the saved SSL certificate radio button.
1283                 savedSslCertificateRadioButton.setChecked(false);
1284             } else {  // Neither SSL certificate is visible.
1285                 // Uncheck both radio buttons.
1286                 savedSslCertificateRadioButton.setChecked(false);
1287                 currentWebsiteCertificateRadioButton.setChecked(false);
1288             }
1289         } else {  // An SSL certificate is not pinned.
1290             // Hide the SSl certificates and instructions.
1291             savedSslCardView.setVisibility(View.GONE);
1292             currentSslCardView.setVisibility(View.GONE);
1293             noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1294
1295             // Uncheck the radio buttons.
1296             savedSslCertificateRadioButton.setChecked(false);
1297             currentWebsiteCertificateRadioButton.setChecked(false);
1298         }
1299
1300         // Set the pinned IP addresses icon.
1301         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.
1302             // Check the switch.
1303             pinnedIpAddressesSwitch.setChecked(true);
1304
1305             // Set the icon according to the theme.
1306             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1307                 pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_night, null));
1308             } else {
1309                 pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_day, null));
1310             }
1311         } else {  // Pinned IP Addresses is disabled.
1312             // Uncheck the switch.
1313             pinnedIpAddressesSwitch.setChecked(false);
1314
1315             // Set the icon according to the theme.
1316             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1317                 pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_night, null));
1318             } else {
1319                 pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_day, null));
1320             }
1321         }
1322
1323         // Populate the saved and current IP addresses.
1324         savedIpAddressesTextView.setText(savedIpAddresses);
1325         currentIpAddressesTextView.setText(DomainsActivity.currentIpAddresses);
1326
1327         // Set the initial display status of the IP addresses card views.
1328         if (pinnedIpAddressesSwitch.isChecked()) {  // IP addresses are pinned.
1329             // Set the visibility of the saved IP addresses.
1330             if (savedIpAddresses == null) {  // There are no saved IP addresses.
1331                 savedIpAddressesCardView.setVisibility(View.GONE);
1332             } else {  // There are saved IP addresses.
1333                 savedIpAddressesCardView.setVisibility(View.VISIBLE);
1334             }
1335
1336             // Set the visibility of the current IP addresses.
1337             currentIpAddressesCardView.setVisibility(View.VISIBLE);
1338
1339             // Set the status of the radio buttons and the card view backgrounds.
1340             if (savedIpAddressesCardView.getVisibility() == View.VISIBLE) {  // The saved IP addresses are displayed.
1341                 // Check the saved IP addresses radio button.
1342                 savedIpAddressesRadioButton.setChecked(true);
1343
1344                 // Uncheck the current IP addresses radio button.
1345                 currentIpAddressesRadioButton.setChecked(false);
1346
1347                 // Darken the background of the current IP addresses linear layout according to the theme.
1348                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1349                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1350                 } else {
1351                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1352                 }
1353             } else {  // The saved IP addresses are hidden.
1354                 // Check the current IP addresses radio button.
1355                 currentIpAddressesRadioButton.setChecked(true);
1356
1357                 // Uncheck the saved IP addresses radio button.
1358                 savedIpAddressesRadioButton.setChecked(false);
1359             }
1360         } else {  // IP addresses are not pinned.
1361             // Hide the IP addresses card views.
1362             savedIpAddressesCardView.setVisibility(View.GONE);
1363             currentIpAddressesCardView.setVisibility(View.GONE);
1364
1365             // Uncheck the radio buttons.
1366             savedIpAddressesRadioButton.setChecked(false);
1367             currentIpAddressesRadioButton.setChecked(false);
1368         }
1369
1370
1371         // Set the JavaScript switch listener.
1372         javaScriptSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1373             if (isChecked) {  // JavaScript is enabled.
1374                 // Update the JavaScript icon.
1375                 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null));
1376
1377                 // Enable the DOM storage `Switch`.
1378                 domStorageSwitch.setEnabled(true);
1379
1380                 // Update the DOM storage icon.
1381                 if (domStorageSwitch.isChecked()) {  // DOM storage is enabled.
1382                     domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_enabled, null));
1383                 } else {  // DOM storage is disabled.
1384                     // Set the icon according to the theme.
1385                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1386                         domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled_night, null));
1387                     } else {
1388                         domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled_day, null));
1389                     }
1390                 }
1391             } else {  // JavaScript is disabled.
1392                 // Update the JavaScript icon.
1393                 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null));
1394
1395                 // Disable the DOM storage `Switch`.
1396                 domStorageSwitch.setEnabled(false);
1397
1398                 // Set the DOM storage icon according to the theme.
1399                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1400                     domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_ghosted_night, null));
1401                 } else {
1402                     domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_ghosted_day, null));
1403                 }
1404             }
1405         });
1406
1407         // Set the first-party cookies switch listener.
1408         firstPartyCookiesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1409             if (isChecked) {  // First-party cookies are enabled.
1410                 // Update the first-party cookies icon.
1411                 firstPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_enabled, null));
1412
1413                 // Enable the third-party cookies switch.
1414                 thirdPartyCookiesSwitch.setEnabled(true);
1415
1416                 // Update the third-party cookies icon.
1417                 if (thirdPartyCookiesSwitch.isChecked()) {  // Third-party cookies are enabled.
1418                     // Set the third-party cookies icon to be red.
1419                     thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_warning, null));
1420                 } else {  // Third-party cookies are disabled.
1421                     // Set the third-party cookies icon according to the theme.
1422                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1423                         thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_night, null));
1424                     } else {
1425                         thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_day, null));
1426                     }
1427                 }
1428             } else {  // First-party cookies are disabled.
1429                 // Update the first-party cookies icon according to the theme.
1430                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1431                     firstPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_night, null));
1432                 } else {
1433                     firstPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_day, null));
1434                 }
1435
1436                 // Disable the third-party cookies switch.
1437                 thirdPartyCookiesSwitch.setEnabled(false);
1438
1439                 // Set the third-party cookies icon according to the theme.
1440                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1441                     thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_ghosted_night, null));
1442                 } else {
1443                     thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_ghosted_day, null));
1444                 }
1445             }
1446         });
1447
1448         // Set the third-party cookies switch listener.
1449         thirdPartyCookiesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1450             // Update the icon.
1451             if (isChecked) {
1452                 // Set the third-party cookies icon to be red.
1453                 thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_warning, null));
1454             } else {
1455                 // Update the third-party cookies icon according to the theme.
1456                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1457                     thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_night, null));
1458                 } else {
1459                     thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_day, null));
1460                 }
1461             }
1462         });
1463
1464         // Set the DOM Storage switch listener.
1465         domStorageSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1466             // Update the icon.
1467             if (isChecked) {
1468                 domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_enabled, null));
1469             } else {
1470                 // Set the icon according to the theme.
1471                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1472                     domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled_night, null));
1473                 } else {
1474                     domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled_day, null));
1475                 }
1476             }
1477         });
1478
1479         // Set the form data switch listener.  It can be removed once the minimum API >= 26.
1480         if (Build.VERSION.SDK_INT < 26) {
1481             formDataSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1482                 // Update the icon.
1483                 if (isChecked) {
1484                     formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_enabled, null));
1485                 } else {
1486                     // Set the icon according to the theme.
1487                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1488                         formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_disabled_night, null));
1489                     } else {
1490                         formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_disabled_day, null));
1491                     }
1492                 }
1493             });
1494         }
1495
1496         // Set the EasyList switch listener.
1497         easyListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1498             // Update the icon.
1499             if (isChecked) {  // EasyList is on.
1500                 // Set the icon according to the theme.
1501                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1502                     easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_night, null));
1503                 } else {
1504                     easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_day, null));
1505                 }
1506             } else {  // EasyList is off.
1507                 // Set the icon according to the theme.
1508                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1509                     easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_night, null));
1510                 } else {
1511                     easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_day, null));
1512                 }
1513             }
1514         });
1515
1516         // Set the EasyPrivacy switch listener.
1517         easyPrivacySwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1518             // Update the icon.
1519             if (isChecked) {  // EasyPrivacy is on.
1520                 // Set the icon according to the theme.
1521                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1522                     easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_night, null));
1523                 } else {
1524                     easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_day, null));
1525                 }
1526             } else {  // EasyPrivacy is off.
1527                 // Set the icon according to the theme.
1528                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1529                     easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_night, null));
1530                 } else {
1531                     easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_day, null));
1532                 }
1533             }
1534         });
1535
1536         // Set the Fanboy's Annoyance List switch listener.
1537         fanboysAnnoyanceListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1538             // Update the icon and Fanboy's Social Blocking List.
1539             if (isChecked) {  // Fanboy's Annoyance List is on.
1540                 // Set the icon according to the theme.
1541                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1542                     fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_night, null));
1543                 } else {
1544                     fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_day, null));
1545                 }
1546
1547                 // Disable the Fanboy's Social Blocking List switch.
1548                 fanboysSocialBlockingListSwitch.setEnabled(false);
1549
1550                 // Update the Fanboy's Social Blocking List icon according to the theme.
1551                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1552                     fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_ghosted_night, null));
1553                 } else {
1554                     fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_ghosted_day, null));
1555                 }
1556             } else {  // Fanboy's Annoyance List is off.
1557                 // Set the icon according to the theme.
1558                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1559                     fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_night, null));
1560                 } else {
1561                     fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_day, null));
1562                 }
1563
1564                 // Enable the Fanboy's Social Blocking List switch.
1565                 fanboysSocialBlockingListSwitch.setEnabled(true);
1566
1567                 // Update the Fanboy's Social Blocking List icon.
1568                 if (fanboysSocialBlockingListSwitch.isChecked()) {  // Fanboy's Social Blocking List is on.
1569                     // Update the icon according to the theme.
1570                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1571                         fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_night, null));
1572                     } else {
1573                         fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_day, null));
1574                     }
1575                 } else {  // Fanboy's Social Blocking List is off.
1576                     // Update the icon according to the theme.
1577                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1578                         fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_night, null));
1579                     } else {
1580                         fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_day, null));
1581                     }
1582                 }
1583             }
1584
1585         });
1586
1587         // Set the Fanboy's Social Blocking List switch listener.
1588         fanboysSocialBlockingListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1589             // Update the icon.
1590             if (isChecked) {  // Fanboy's Social Blocking List is on.
1591                 // Set the icon according to the theme.
1592                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1593                     fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_night, null));
1594                 } else {
1595                     fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_day, null));
1596                 }
1597             } else {  // Fanboy's Social Blocking List is off.
1598                 // Set the icon according to the theme.
1599                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1600                     fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_night, null));
1601                 } else {
1602                     fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_day, null));
1603                 }
1604             }
1605         });
1606
1607         // Set the UltraList switch listener.
1608         ultraListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1609             // Update the icon.
1610             if (isChecked) {  // UltraList is on.
1611                 // Set the icon according to the theme.
1612                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1613                     ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_night, null));
1614                 } else {
1615                     ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_day, null));
1616                 }
1617             } else {  // UltraList is off.
1618                 // Set the icon according to the theme.
1619                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1620                     ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_night, null));
1621                 } else {
1622                     ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_day, null));
1623                 }
1624             }
1625         });
1626
1627         // Set the UltraPrivacy switch listener.
1628         ultraPrivacySwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1629             // Update the icon.
1630             if (isChecked) {  // UltraPrivacy is on.
1631                 // Set the icon according to the theme.
1632                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1633                     ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_night, null));
1634                 } else {
1635                     ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_day, null));
1636                 }
1637             } else {  // UltraPrivacy is off.
1638                 // Set the icon according to the theme.
1639                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1640                     ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_night, null));
1641                 } else {
1642                     ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_day, null));
1643                 }
1644             }
1645         });
1646
1647         // Set the block all third-party requests switch listener.
1648         blockAllThirdPartyRequestsSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1649             // Update the icon.
1650             if (isChecked) {  // Blocking all third-party requests is on.
1651                 // Set the icon according to the theme.
1652                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1653                     blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_enabled_night, null));
1654                 } else {
1655                     blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_enabled_day, null));
1656                 }
1657             } else {  // Blocking all third-party requests is off.
1658                 // Set the icon according to the theme.
1659                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1660                     blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_disabled_night, null));
1661                 } else {
1662                     blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_disabled_day, null));
1663                 }
1664             }
1665         });
1666
1667         // Set the user agent spinner listener.
1668         userAgentSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1669             @Override
1670             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1671                 // Set the new user agent.
1672                 switch (position) {
1673                     case MainWebViewActivity.DOMAINS_SYSTEM_DEFAULT_USER_AGENT:
1674                         // Show the user agent TextView.
1675                         userAgentTextView.setVisibility(View.VISIBLE);
1676
1677                         // Hide the custom user agent EditText.
1678                         customUserAgentEditText.setVisibility(View.GONE);
1679
1680                         // Set the user text.
1681                         switch (defaultUserAgentArrayPosition) {
1682                             case MainWebViewActivity.UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
1683                                 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
1684                                 userAgentTextView.setText(defaultUserAgentName);
1685                                 break;
1686
1687                             case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
1688                                 // Display the `WebView` default user agent.
1689                                 userAgentTextView.setText(webViewDefaultUserAgentString);
1690                                 break;
1691
1692                             case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
1693                                 // Display the custom user agent.
1694                                 userAgentTextView.setText(defaultCustomUserAgentString);
1695                                 break;
1696
1697                             default:
1698                                 // Get the user agent string from the user agent data array.
1699                                 userAgentTextView.setText(userAgentDataArray[defaultUserAgentArrayPosition]);
1700                         }
1701                         break;
1702
1703                     case MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT:
1704                         // Show the user agent TextView and set the text.
1705                         userAgentTextView.setVisibility(View.VISIBLE);
1706                         userAgentTextView.setText(webViewDefaultUserAgentString);
1707
1708                         // Hide the custom user agent EditTex.
1709                         customUserAgentEditText.setVisibility(View.GONE);
1710                         break;
1711
1712                     case MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT:
1713                         // Hide the user agent TextView.
1714                         userAgentTextView.setVisibility(View.GONE);
1715
1716                         // Show the custom user agent EditText and set the current user agent name as the text.
1717                         customUserAgentEditText.setVisibility(View.VISIBLE);
1718                         customUserAgentEditText.setText(currentUserAgentName);
1719                         break;
1720
1721                     default:
1722                         // 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.
1723                         userAgentTextView.setVisibility(View.VISIBLE);
1724                         userAgentTextView.setText(userAgentDataArray[position - 1]);
1725
1726                         // Hide `customUserAgentEditText`.
1727                         customUserAgentEditText.setVisibility(View.GONE);
1728                 }
1729             }
1730
1731             @Override
1732             public void onNothingSelected(AdapterView<?> parent) {
1733                 // Do nothing.
1734             }
1735         });
1736
1737         // Set the font size spinner listener.
1738         fontSizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1739             @Override
1740             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1741                 // Update the font size display options.
1742                 if (position == 0) {  // The system default font size has been selected.
1743                     // Show the default font size text view.
1744                     defaultFontSizeTextView.setVisibility(View.VISIBLE);
1745
1746                     // Hide the custom font size edit text.
1747                     customFontSizeEditText.setVisibility(View.GONE);
1748                 } else {  // A custom font size has been selected.
1749                     // Hide the default font size text view.
1750                     defaultFontSizeTextView.setVisibility(View.GONE);
1751
1752                     // Show the custom font size edit text.
1753                     customFontSizeEditText.setVisibility(View.VISIBLE);
1754                 }
1755             }
1756
1757             @Override
1758             public void onNothingSelected(AdapterView<?> parent) {
1759                 // Do nothing.
1760             }
1761         });
1762
1763         // Set the swipe to refresh spinner listener.
1764         swipeToRefreshSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1765             @Override
1766             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1767                 // Update the icon and the visibility of `nightModeTextView`.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
1768                 switch (position) {
1769                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1770                         if (defaultSwipeToRefresh) {  // Swipe to refresh enabled by default.
1771                             // Set the icon according to the theme.
1772                             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1773                                 swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_night, null));
1774                             } else {
1775                                 swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_day, null));
1776                             }
1777                         } else {  // Swipe to refresh disabled by default.
1778                             // Set the icon according to the theme.
1779                             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1780                                 swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_night, null));
1781                             } else {
1782                                 swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_day, null));
1783                             }
1784                         }
1785
1786                         // Show the swipe to refresh TextView.
1787                         swipeToRefreshTextView.setVisibility(View.VISIBLE);
1788                         break;
1789
1790                     case DomainsDatabaseHelper.ENABLED:
1791                         // Set the icon according to the theme.
1792                         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1793                             swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_night, null));
1794                         } else {
1795                             swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_day, null));
1796                         }
1797
1798                         // Hide the swipe to refresh TextView.
1799                         swipeToRefreshTextView.setVisibility(View.GONE);
1800                         break;
1801
1802                     case DomainsDatabaseHelper.DISABLED:
1803                         // Set the icon according to the theme.
1804                         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1805                             swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_night, null));
1806                         } else {
1807                             swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_day, null));
1808                         }
1809
1810                         // Hide the swipe to refresh TextView.
1811                         swipeToRefreshTextView.setVisibility(View.GONE);
1812                 }
1813             }
1814
1815             @Override
1816             public void onNothingSelected(AdapterView<?> parent) {
1817                 // Do nothing.
1818             }
1819         });
1820
1821         // Set the WebView theme spinner listener.
1822         webViewThemeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1823             @Override
1824             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1825                 // 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.
1826                 switch (position) {
1827                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // the domain WebView theme is system default.
1828                         // Set the icon according to the app WebView theme.
1829                         switch (appWebViewThemeEntryNumber) {
1830                             case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // The default WebView theme is system default.
1831                                 // Set the icon according to the app theme.
1832                                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
1833                                     // Set the light mode icon.
1834                                     webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_day, null));
1835                                 } else {
1836                                     // Set the dark theme icon.
1837                                     webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_night, null));
1838                                 }
1839                                 break;
1840
1841                             case DomainsDatabaseHelper.LIGHT_THEME:  // The default WebView theme is light.
1842                                 // Set the icon according to the app theme.
1843                                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
1844                                     webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_day, null));
1845                                 } else {
1846                                     webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_night, null));
1847                                 }
1848                                 break;
1849
1850                             case DomainsDatabaseHelper.DARK_THEME:  // The default WebView theme is dark.
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_dark_theme_day, null));
1854                                 } else {
1855                                     webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_night, null));
1856                                 }
1857                                 break;
1858                         }
1859
1860                         // Show the WebView theme text view.
1861                         webViewThemeTextView.setVisibility(View.VISIBLE);
1862                         break;
1863
1864                     case DomainsDatabaseHelper.LIGHT_THEME:  // The domain WebView theme is light.
1865                         // Set the icon according to the app theme.
1866                         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
1867                             webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_day, null));
1868                         } else {
1869                             webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_night, null));
1870                         }
1871
1872                         // Hide the WebView theme text view.
1873                         webViewThemeTextView.setVisibility(View.GONE);
1874                         break;
1875
1876                     case DomainsDatabaseHelper.DARK_THEME:  // The domain WebView theme is dark.
1877                         // Set the icon according to the app theme.
1878                         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
1879                             webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_day, null));
1880                         } else {
1881                             webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_night, null));
1882                         }
1883
1884                         // Hide the WebView theme text view.
1885                         webViewThemeTextView.setVisibility(View.GONE);
1886                         break;
1887                 }
1888             }
1889
1890             @Override
1891             public void onNothingSelected(AdapterView<?> parent) {
1892                 // Do nothing.
1893             }
1894         });
1895
1896         // Set the wide viewport spinner listener.
1897         wideViewportSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1898             @Override
1899             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1900                 // Update the icon and the visibility of the wide viewport text view.
1901                 switch (position) {
1902                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1903                         if (defaultWideViewport) {  // Wide viewport is enabled by default.
1904                             // Set the icon according to the theme.
1905                             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1906                                 wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_night, null));
1907                             } else {
1908                                 wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_day, null));
1909                             }
1910                         } else {  // Wide viewport is disabled by default.
1911                             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1912                                 wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_night, null));
1913                             } else {
1914                                 wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_day, null));
1915                             }
1916                         }
1917
1918                         // Show the wide viewport text view.
1919                         wideViewportTextView.setVisibility(View.VISIBLE);
1920                         break;
1921
1922                     case DomainsDatabaseHelper.ENABLED:
1923                         // Set the icon according to the theme.
1924                         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1925                             wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_night, null));
1926                         } else {
1927                             wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_day, null));
1928                         }
1929
1930                         // Hide the wide viewport text view.
1931                         wideViewportTextView.setVisibility(View.GONE);
1932                         break;
1933
1934                     case DomainsDatabaseHelper.DISABLED:
1935                         // Set the icon according to the theme.
1936                         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1937                             wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_night, null));
1938                         } else {
1939                             wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_day, null));
1940                         }
1941
1942                         // Hid ethe wide viewport text view.
1943                         wideViewportTextView.setVisibility(View.GONE);
1944                         break;
1945                 }
1946             }
1947
1948             @Override
1949             public void onNothingSelected(AdapterView<?> parent) {
1950                 // Do nothing.
1951             }
1952         });
1953
1954         // Set the display webpage images spinner listener.
1955         displayWebpageImagesSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1956             @Override
1957             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1958                 // Update the icon and the visibility of the display images text view.
1959                 switch (position) {
1960                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1961                         if (defaultDisplayWebpageImages) {  // Display webpage images is enabled by default.
1962                             // Set the icon according to the theme.
1963                             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1964                                 displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_night, null));
1965                             } else {
1966                                 displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_day, null));
1967                             }
1968                         } else {  // Display webpage images is disabled by default.
1969                             // Set the icon according to the theme.
1970                             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1971                                 displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_night, null));
1972                             } else {
1973                                 displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_day, null));
1974                             }
1975                         }
1976
1977                         // Show the display images text view.
1978                         displayImagesTextView.setVisibility(View.VISIBLE);
1979                         break;
1980
1981                     case DomainsDatabaseHelper.ENABLED:
1982                         // Set the icon according to the theme.
1983                         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1984                             displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_night, null));
1985                         } else {
1986                             displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_day, null));
1987                         }
1988
1989                         // Hide the display images text view.
1990                         displayImagesTextView.setVisibility(View.GONE);
1991                         break;
1992
1993                     case DomainsDatabaseHelper.DISABLED:
1994                         // Set the icon according to the theme.
1995                         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1996                             displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_night, null));
1997                         } else {
1998                             displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_day, null));
1999                         }
2000
2001                         // Hide the display images text view.
2002                         displayImagesTextView.setVisibility(View.GONE);
2003                         break;
2004                 }
2005             }
2006
2007             @Override
2008             public void onNothingSelected(AdapterView<?> parent) {
2009                 // Do nothing.
2010             }
2011         });
2012         
2013         // Set the pinned SSL certificate switch listener.
2014         pinnedSslCertificateSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
2015             // Update the icon.
2016             if (isChecked) {  // SSL certificate pinning is enabled.
2017                 // Set the icon according to the theme.
2018                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2019                     pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_night, null));
2020                 } else {
2021                     pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_day, null));
2022                 }
2023
2024                 // Update the visibility of the saved SSL certificate.
2025                 if (savedSslIssuedToCNameString == null) {
2026                     savedSslCardView.setVisibility(View.GONE);
2027                 } else {
2028                     savedSslCardView.setVisibility(View.VISIBLE);
2029                 }
2030
2031                 // Update the visibility of the current website SSL certificate.
2032                 if (DomainsActivity.sslIssuedToCName == null) {
2033                     // Hide the SSL certificate.
2034                     currentSslCardView.setVisibility(View.GONE);
2035
2036                     // Show the instruction.
2037                     noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
2038                 } else {
2039                     // Show the SSL certificate.
2040                     currentSslCardView.setVisibility(View.VISIBLE);
2041
2042                     // Hide the instruction.
2043                     noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
2044                 }
2045
2046                 // Set the status of the radio buttons.
2047                 if (savedSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
2048                     // Check the saved SSL certificate radio button.
2049                     savedSslCertificateRadioButton.setChecked(true);
2050
2051                     // Uncheck the current website SSL certificate radio button.
2052                     currentWebsiteCertificateRadioButton.setChecked(false);
2053
2054                     // Set the background of the saved SSL certificate linear layout to be transparent.
2055                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
2056
2057                     // Darken the background of the current website SSL certificate linear layout according to the theme.
2058                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2059                         currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2060                     } else {
2061                         currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2062                     }
2063
2064                     // Scroll to the current website SSL certificate card.
2065                     savedSslCardView.getParent().requestChildFocus(savedSslCardView, savedSslCardView);
2066                 } else if (currentSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
2067                     // Check the current website SSL certificate radio button.
2068                     currentWebsiteCertificateRadioButton.setChecked(true);
2069
2070                     // Uncheck the saved SSL certificate radio button.
2071                     savedSslCertificateRadioButton.setChecked(false);
2072
2073                     // Set the background of the current website SSL certificate linear layout to be transparent.
2074                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
2075
2076                     // Darken the background of the saved SSL certificate linear layout according to the theme.
2077                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2078                         savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2079                     } else {
2080                         savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2081                     }
2082
2083                     // Scroll to the current website SSL certificate card.
2084                     currentSslCardView.getParent().requestChildFocus(currentSslCardView, currentSslCardView);
2085                 } else {  // Neither SSL certificate is visible.
2086                     // Uncheck both radio buttons.
2087                     savedSslCertificateRadioButton.setChecked(false);
2088                     currentWebsiteCertificateRadioButton.setChecked(false);
2089
2090                     // Scroll to the current website SSL certificate card.
2091                     noCurrentWebsiteCertificateTextView.getParent().requestChildFocus(noCurrentWebsiteCertificateTextView, noCurrentWebsiteCertificateTextView);
2092                 }
2093             } else {  // SSL certificate pinning is disabled.
2094                 // Set the icon according to the theme.
2095                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2096                     pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_night, null));
2097                 } else {
2098                     pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_day, null));
2099                 }
2100
2101                 // Hide the SSl certificates and instructions.
2102                 savedSslCardView.setVisibility(View.GONE);
2103                 currentSslCardView.setVisibility(View.GONE);
2104                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
2105
2106                 // Uncheck the radio buttons.
2107                 savedSslCertificateRadioButton.setChecked(false);
2108                 currentWebsiteCertificateRadioButton.setChecked(false);
2109             }
2110         });
2111
2112         savedSslCardView.setOnClickListener((View view) -> {
2113             // Check the saved SSL certificate radio button.
2114             savedSslCertificateRadioButton.setChecked(true);
2115
2116             // Uncheck the current website SSL certificate radio button.
2117             currentWebsiteCertificateRadioButton.setChecked(false);
2118
2119             // Set the background of the saved SSL certificate linear layout to be transparent.
2120             savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
2121
2122             // Darken the background of the current website SSL certificate linear layout according to the theme.
2123             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2124                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2125             } else {
2126                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2127             }
2128         });
2129
2130         savedSslCertificateRadioButton.setOnClickListener((View view) -> {
2131             // Check the saved SSL certificate radio button.
2132             savedSslCertificateRadioButton.setChecked(true);
2133
2134             // Uncheck the current website SSL certificate radio button.
2135             currentWebsiteCertificateRadioButton.setChecked(false);
2136
2137             // Set the background of the saved SSL certificate linear layout to be transparent.
2138             savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
2139
2140             // Darken the background of the current website SSL certificate linear layout according to the theme.
2141             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2142                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2143             } else {
2144                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2145             }
2146         });
2147
2148         currentSslCardView.setOnClickListener((View view) -> {
2149             // Check the current website SSL certificate radio button.
2150             currentWebsiteCertificateRadioButton.setChecked(true);
2151
2152             // Uncheck the saved SSL certificate radio button.
2153             savedSslCertificateRadioButton.setChecked(false);
2154
2155             // Set the background of the current website SSL certificate linear layout to be transparent.
2156             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
2157
2158             // Darken the background of the saved SSL certificate linear layout according to the theme.
2159             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2160                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2161             } else {
2162                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2163             }
2164         });
2165
2166         currentWebsiteCertificateRadioButton.setOnClickListener((View view) -> {
2167             // Check the current website SSL certificate radio button.
2168             currentWebsiteCertificateRadioButton.setChecked(true);
2169
2170             // Uncheck the saved SSL certificate radio button.
2171             savedSslCertificateRadioButton.setChecked(false);
2172
2173             // Set the background of the current website SSL certificate linear layout to be transparent.
2174             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
2175
2176             // Darken the background of the saved SSL certificate linear layout according to the theme.
2177             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2178                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2179             } else {
2180                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2181             }
2182         });
2183
2184         // Set the pinned IP addresses switch listener.
2185         pinnedIpAddressesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
2186             // Update the icon.
2187             if (isChecked) {  // IP addresses pinning is enabled.
2188                 // Set the icon according to the theme.
2189                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2190                     pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_night, null));
2191                 } else {
2192                     pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_day, null));
2193                 }
2194
2195                 // Update the visibility of the saved IP addresses card view.
2196                 if (savedIpAddresses == null) {  // There are no saved IP addresses.
2197                     savedIpAddressesCardView.setVisibility(View.GONE);
2198                 } else {  // There are saved IP addresses.
2199                     savedIpAddressesCardView.setVisibility(View.VISIBLE);
2200                 }
2201
2202                 // Show the current IP addresses card view.
2203                 currentIpAddressesCardView.setVisibility(View.VISIBLE);
2204
2205                 // Set the status of the radio buttons.
2206                 if (savedIpAddressesCardView.getVisibility() == View.VISIBLE) {  // The saved IP addresses are visible.
2207                     // Check the saved IP addresses radio button.
2208                     savedIpAddressesRadioButton.setChecked(true);
2209
2210                     // Uncheck the current IP addresses radio button.
2211                     currentIpAddressesRadioButton.setChecked(false);
2212
2213                     // Set the background of the saved IP addresses linear layout to be transparent.
2214                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
2215
2216                     // Darken the background of the current IP addresses linear layout according to the theme.
2217                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2218                         currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2219                     } else {
2220                         currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2221                     }
2222                 } else {  // The saved IP addresses are not visible.
2223                     // Check the current IP addresses radio button.
2224                     currentIpAddressesRadioButton.setChecked(true);
2225
2226                     // Uncheck the saved IP addresses radio button.
2227                     savedIpAddressesRadioButton.setChecked(false);
2228
2229                     // Set the background of the current IP addresses linear layout to be transparent.
2230                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2231
2232                     // Darken the background of the saved IP addresses linear layout according to the theme.
2233                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2234                         savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2235                     } else {
2236                         savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2237                     }
2238                 }
2239
2240                 // Scroll to the bottom of the card views.
2241                 currentIpAddressesCardView.getParent().requestChildFocus(currentIpAddressesCardView, currentIpAddressesCardView);
2242             } else {  // IP addresses pinning is disabled.
2243                 // Set the icon according to the theme.
2244                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2245                     pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_night, null));
2246                 } else {
2247                     pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_day, null));
2248                 }
2249
2250                 // Hide the IP addresses card views.
2251                 savedIpAddressesCardView.setVisibility(View.GONE);
2252                 currentIpAddressesCardView.setVisibility(View.GONE);
2253
2254                 // Uncheck the radio buttons.
2255                 savedIpAddressesRadioButton.setChecked(false);
2256                 currentIpAddressesRadioButton.setChecked(false);
2257             }
2258         });
2259
2260         savedIpAddressesCardView.setOnClickListener((View view) -> {
2261             // Check the saved IP addresses radio button.
2262             savedIpAddressesRadioButton.setChecked(true);
2263
2264             // Uncheck the current website IP addresses radio button.
2265             currentIpAddressesRadioButton.setChecked(false);
2266
2267             // Set the background of the saved IP addresses linear layout to be transparent.
2268             savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2269
2270             // Darken the background of the current IP addresses linear layout according to the theme.
2271             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2272                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2273             } else {
2274                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2275             }
2276         });
2277
2278         savedIpAddressesRadioButton.setOnClickListener((View view) -> {
2279             // Check the saved IP addresses radio button.
2280             savedIpAddressesRadioButton.setChecked(true);
2281
2282             // Uncheck the current website IP addresses radio button.
2283             currentIpAddressesRadioButton.setChecked(false);
2284
2285             // Set the background of the saved IP addresses linear layout to be transparent.
2286             savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2287
2288             // Darken the background of the current IP addresses linear layout according to the theme.
2289             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2290                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2291             } else {
2292                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2293             }
2294         });
2295
2296         currentIpAddressesCardView.setOnClickListener((View view) -> {
2297             // Check the current IP addresses radio button.
2298             currentIpAddressesRadioButton.setChecked(true);
2299
2300             // Uncheck the saved IP addresses radio button.
2301             savedIpAddressesRadioButton.setChecked(false);
2302
2303             // Set the background of the current IP addresses linear layout to be transparent.
2304             currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2305
2306             // Darken the background of the saved IP addresses linear layout according to the theme.
2307             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2308                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2309             } else {
2310                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2311             }
2312         });
2313
2314         currentIpAddressesRadioButton.setOnClickListener((View view) -> {
2315             // Check the current IP addresses radio button.
2316             currentIpAddressesRadioButton.setChecked(true);
2317
2318             // Uncheck the saved IP addresses radio button.
2319             savedIpAddressesRadioButton.setChecked(false);
2320
2321             // Set the background of the current IP addresses linear layout to be transparent.
2322             currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
2323
2324             // Darken the background of the saved IP addresses linear layout according to the theme.
2325             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
2326                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
2327             } else {
2328                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
2329             }
2330         });
2331
2332         // Set the scroll Y.
2333         domainSettingsScrollView.post(() -> domainSettingsScrollView.setScrollY(scrollY));
2334
2335         // Return the domain settings view.
2336         return domainSettingsView;
2337     }
2338
2339     private boolean checkDomainNameAgainstCertificate(String domainName, String certificateCommonName) {
2340         // Initialize `domainNamesMatch`.
2341         boolean domainNamesMatch = false;
2342
2343         // Check various wildcard permutations if `domainName` and `certificateCommonName` are not empty.
2344         // `noinspection ConstantCondition` removes Android Studio's incorrect lint warning that `domainName` can never be `null`.
2345         if ((domainName != null) && (certificateCommonName != null)) {
2346             // Check if the domains match.
2347             if (domainName.equals(certificateCommonName)) {
2348                 domainNamesMatch = true;
2349             }
2350
2351             // If `domainName` starts with a wildcard, check the base domain against all the subdomains of `certificateCommonName`.
2352             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2)) {
2353                 // Remove the initial `*.`.
2354                 String baseDomainName = domainName.substring(2);
2355
2356                 // Setup a copy of `certificateCommonName` to test subdomains.
2357                 String certificateCommonNameSubdomain = certificateCommonName;
2358
2359                 // Check all the subdomains in `certificateCommonNameSubdomain` against `baseDomainName`.
2360                 while (!domainNamesMatch && certificateCommonNameSubdomain.contains(".")) {  // Stop checking if we know that `domainNamesMatch` is `true` or if we run out of  `.`.
2361                     // Test the `certificateCommonNameSubdomain` against `baseDomainName`.
2362                     if (certificateCommonNameSubdomain.equals(baseDomainName)) {
2363                         domainNamesMatch = true;
2364                     }
2365
2366                     // Strip out the lowest subdomain of `certificateCommonNameSubdomain`.
2367                     try {
2368                         certificateCommonNameSubdomain = certificateCommonNameSubdomain.substring(certificateCommonNameSubdomain.indexOf(".") + 1);
2369                     } catch (IndexOutOfBoundsException e) {  // `certificateCommonNameSubdomain` ends with `.`.
2370                         certificateCommonNameSubdomain = "";
2371                     }
2372                 }
2373             }
2374
2375             // If `certificateCommonName` starts with a wildcard, check the base common name against all the subdomains of `domainName`.
2376             if (!domainNamesMatch && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
2377                 // Remove the initial `*.`.
2378                 String baseCertificateCommonName = certificateCommonName.substring(2);
2379
2380                 // Setup a copy of `domainName` to test subdomains.
2381                 String domainNameSubdomain = domainName;
2382
2383                 // Check all the subdomains in `domainNameSubdomain` against `baseCertificateCommonName`.
2384                 while (!domainNamesMatch && domainNameSubdomain.contains(".") && (domainNameSubdomain.length() > 2)) {
2385                     // Test the `domainNameSubdomain` against `baseCertificateCommonName`.
2386                     if (domainNameSubdomain.equals(baseCertificateCommonName)) {
2387                         domainNamesMatch = true;
2388                     }
2389
2390                     // Strip out the lowest subdomain of `domainNameSubdomain`.
2391                     try {
2392                         domainNameSubdomain = domainNameSubdomain.substring(domainNameSubdomain.indexOf(".") + 1);
2393                     } catch (IndexOutOfBoundsException e) { // `domainNameSubdomain` ends with `.`.
2394                         domainNameSubdomain = "";
2395                     }
2396                 }
2397             }
2398
2399             // If both names start with a wildcard, check if the root of one contains the root of the other.
2400             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2) && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
2401                 // Remove the wildcards.
2402                 String rootDomainName = domainName.substring(2);
2403                 String rootCertificateCommonName = certificateCommonName.substring(2);
2404
2405                 // Check if one name ends with the contents of the other.  If so, there will be overlap in the their wildcard subdomains.
2406                 if (rootDomainName.endsWith(rootCertificateCommonName) || rootCertificateCommonName.endsWith(rootDomainName)) {
2407                     domainNamesMatch = true;
2408                 }
2409             }
2410         }
2411
2412         return domainNamesMatch;
2413     }
2414 }