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