]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java
Fix custom user agents. https://redmine.stoutner.com/issues/810
[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 color spans.
303         final ForegroundColorSpan blueColorSpan = new ForegroundColorSpan(context.getColor(R.color.blue_text));
304         final ForegroundColorSpan redColorSpan = new ForegroundColorSpan(context.getColor(R.color.red_text));
305
306         // Set the domain name from the the database cursor.
307         domainNameEditText.setText(domainNameString);
308
309         // Update the certificates' `Common Name` color when the domain name text changes.
310         domainNameEditText.addTextChangedListener(new TextWatcher() {
311             @Override
312             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
313                 // Do nothing.
314             }
315
316             @Override
317             public void onTextChanged(CharSequence s, int start, int before, int count) {
318                 // Do nothing.
319             }
320
321             @Override
322             public void afterTextChanged(Editable s) {
323                 // Get the new domain name.
324                 String newDomainName = domainNameEditText.getText().toString();
325
326                 // Check the saved SSL certificate against the new domain name.
327                 boolean savedSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, savedSslIssuedToCNameString);
328
329                 // Create a `SpannableStringBuilder` for the saved certificate `Common Name`.
330                 SpannableStringBuilder savedSslCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString);
331
332                 // Format the saved certificate `Common Name` color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
333                 if (savedSslMatchesNewDomainName) {
334                     savedSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
335                 } else {
336                     savedSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), savedSslCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
337                 }
338
339                 // Update the saved SSL issued to CName text view.
340                 savedSslIssuedToCNameTextView.setText(savedSslCNameStringBuilder);
341
342                 // Update the current website certificate if it exists.
343                 if (DomainsActivity.sslIssuedToCName != null) {
344                     // Check the current website certificate against the new domain name.
345                     boolean currentSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, DomainsActivity.sslIssuedToCName);
346
347                     // Create a `SpannableStringBuilder` for the current website certificate `Common Name`.
348                     SpannableStringBuilder currentSslCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName);
349
350                     // Format the current certificate `Common Name` color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
351                     if (currentSslMatchesNewDomainName) {
352                         currentSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
353                     } else {
354                         currentSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), currentSslCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
355                     }
356
357                     // Update the current SSL issued to CName text view.
358                     currentSslIssuedToCNameTextView.setText(currentSslCNameStringBuilder);
359                 }
360             }
361         });
362
363         // Set the JavaScript switch status.
364         if (javaScriptInt == 1) {  // JavaScript is enabled.
365             javaScriptSwitch.setChecked(true);
366             javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null));
367         } else {  // JavaScript is disabled.
368             javaScriptSwitch.setChecked(false);
369             javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null));
370         }
371
372         // Set the cookies switch status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
373         // Doing this makes no sense until it can also be done with the preferences.
374         if (cookiesInt == 1) {  // Cookies are enabled.
375             // Turn the switch on.
376             cookiesSwitch.setChecked(true);
377
378             // Set the icon.
379             cookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_enabled, null));
380         } else {  // Cookies are disabled.
381             // Turn the switch off
382             cookiesSwitch.setChecked(false);
383
384             // Set the icon.
385             cookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled, null));
386         }
387
388         // Only enable DOM storage if JavaScript is enabled.
389         if (javaScriptInt == 1) {  // JavaScript is enabled.
390             // Enable the DOM storage switch.
391             domStorageSwitch.setEnabled(true);
392
393             // Set the DOM storage status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
394             // Doing this makes no sense until it can also be done with the preferences.
395             if (domStorageInt == 1) {  // Both JavaScript and DOM storage are enabled.
396                 domStorageSwitch.setChecked(true);
397                 domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_enabled, null));
398             } else {  // JavaScript is enabled but DOM storage is disabled.
399                 // Set the DOM storage switch to off.
400                 domStorageSwitch.setChecked(false);
401
402                 // Set the icon.
403                 domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled, null));
404             }
405         } else {  // JavaScript is disabled.
406             // Disable the DOM storage switch.
407             domStorageSwitch.setEnabled(false);
408
409             // Set the checked status of DOM storage.
410             domStorageSwitch.setChecked(domStorageInt == 1);
411
412             // Set the icon.
413             domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_ghosted, null));
414         }
415
416         // Set the form data visibility.  Form data can be removed once the minimum API >= 26.
417         if (Build.VERSION.SDK_INT >= 26) {  // Form data no longer applies to newer versions of Android.
418             // Hide the form data image view and switch.
419             formDataImageView.setVisibility(View.GONE);
420             formDataSwitch.setVisibility(View.GONE);
421         } else {  // Form data should be displayed because this is an older version of Android.
422             if (formDataInt == 1) {  // Form data is on.
423                 // Turn the form data switch on.
424                 formDataSwitch.setChecked(true);
425
426                 // Set the form data icon.
427                 formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_enabled, null));
428             } else {  // Form data is off.
429                 // Turn the form data switch to off.
430                 formDataSwitch.setChecked(false);
431
432                 // Set the icon.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
433                 // Doing this makes no sense until it can also be done with the preferences.
434                 formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_disabled, null));
435             }
436         }
437
438         // Set the EasyList status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
439         // Doing this makes no sense until it can also be done with the preferences.
440         if (easyListInt == 1) {  // EasyList is on.
441             // Turn the switch on.
442             easyListSwitch.setChecked(true);
443
444             // Set the icon.
445             easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled, null));
446         } else {  // EasyList is off.
447             // Turn the switch off.
448             easyListSwitch.setChecked(false);
449
450             // Set the icon.
451             easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled, null));
452         }
453
454         // Set the EasyPrivacy status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
455         // Doing this makes no sense until it can also be done with the preferences.
456         if (easyPrivacyInt == 1) {  // EasyPrivacy is on.
457             // Turn the switch on.
458             easyPrivacySwitch.setChecked(true);
459
460             // Set the icon.
461             easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled, null));
462         } else {  // EasyPrivacy is off.
463             // Turn the switch off.
464             easyPrivacySwitch.setChecked(false);
465
466             // Set the icon.
467             easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled, null));
468         }
469
470         // 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.
471         // Doing this makes no sense until it can also be done with the preferences.
472         if (fanboysAnnoyanceListInt == 1) {  // Fanboy's Annoyance List is on.
473             // Turn the switch on.
474             fanboysAnnoyanceListSwitch.setChecked(true);
475
476             // Set the icon.
477             fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled, null));
478         } else {  // Fanboy's Annoyance List is off.
479             // Turn the switch off.
480             fanboysAnnoyanceListSwitch.setChecked(false);
481
482             // Set the icon.
483             fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled, null));
484         }
485
486         // Only enable Fanboy's Social Blocking List if Fanboy's Annoyance List is off.
487         if (fanboysAnnoyanceListInt == 0) {  // Fanboy's Annoyance List is on.
488             // Enable Fanboy's Social Blocking List switch.
489             fanboysSocialBlockingListSwitch.setEnabled(true);
490
491             // 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.
492             // Doing this makes no sense until it can also be done with the preferences.
493             if (fanboysSocialBlockingListInt == 1) {  // Fanboy's Social Blocking List is on.
494                 // Turn on Fanboy's Social Blocking List switch.
495                 fanboysSocialBlockingListSwitch.setChecked(true);
496
497                 // Set the icon.
498                 fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled, null));
499             } else {  // Fanboy's Social Blocking List is off.
500                 // Turn off Fanboy's Social Blocking List switch.
501                 fanboysSocialBlockingListSwitch.setChecked(false);
502
503                 // Set the icon.
504                 fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled, null));
505             }
506         } else {  // Fanboy's Annoyance List is on.
507             // Disable Fanboy's Social Blocking List switch.
508             fanboysSocialBlockingListSwitch.setEnabled(false);
509
510             // Set the status of Fanboy's Social Blocking List.
511             fanboysSocialBlockingListSwitch.setChecked(fanboysSocialBlockingListInt == 1);
512
513             // Set the icon.
514             fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_ghosted, null));
515         }
516
517         // Set the UltraList status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
518         // Doing this makes no sense until it can also be done with the preferences.
519         if (ultraListInt == 1) {  // UltraList is on.
520             // Turn the switch on.
521             ultraListSwitch.setChecked(true);
522
523             // Set the icon.
524             ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled, null));
525         } else {  // UltraList is off.
526             // Turn the switch off.
527             ultraListSwitch.setChecked(false);
528
529             // Set the icon.
530             ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled, null));
531         }
532
533         // Set the UltraPrivacy status.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
534         // Doing this makes no sense until it can also be done with the preferences.
535         if (ultraPrivacyInt == 1) {  // UltraPrivacy is on.
536             // Turn the switch on.
537             ultraPrivacySwitch.setChecked(true);
538
539             // Set the icon.
540             ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled, null));
541         } else {  // EasyPrivacy is off.
542             // Turn the switch off.
543             ultraPrivacySwitch.setChecked(false);
544
545             // Set the icon.
546             ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled, null));
547         }
548
549         // 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.
550         // Doing this makes no sense until it can also be done with the preferences.
551         if (blockAllThirdPartyRequestsInt == 1) {  // Blocking all third-party requests is on.
552             // Turn the switch on.
553             blockAllThirdPartyRequestsSwitch.setChecked(true);
554
555             // Set the icon.
556             blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_enabled, null));
557         } else {  // Blocking all third-party requests is off.
558             // Turn the switch off.
559             blockAllThirdPartyRequestsSwitch.setChecked(false);
560
561             // Set the icon.
562             blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_disabled, null));
563         }
564
565         // Inflated a WebView to get the default user agent.
566         // `@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.
567         @SuppressLint("InflateParams") View bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false);
568         WebView bareWebView = bareWebViewLayout.findViewById(R.id.bare_webview);
569         final String webViewDefaultUserAgentString = bareWebView.getSettings().getUserAgentString();
570
571         // Get a handle for the user agent array adapter.  This array does not contain the `System default` entry.
572         ArrayAdapter<CharSequence> userAgentNamesArray = ArrayAdapter.createFromResource(context, R.array.user_agent_names, R.layout.spinner_item);
573
574         // Get the positions of the user agent and the default user agent.
575         int userAgentArrayPosition = userAgentNamesArray.getPosition(currentUserAgentName);
576         int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
577
578         // Get a handle for the user agent data array.  This array does not contain the `System default` entry.
579         String[] userAgentDataArray = resources.getStringArray(R.array.user_agent_data);
580
581         // Set the user agent text.
582         if (currentUserAgentName.equals(getString(R.string.system_default_user_agent))) {  // Use the system default user agent.
583             // Set the user agent according to the system default.
584             switch (defaultUserAgentArrayPosition) {
585                 case MainWebViewActivity.UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
586                     // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
587                     userAgentTextView.setText(defaultUserAgentName);
588                     break;
589
590                 case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
591                     // Display the `WebView` default user agent.
592                     userAgentTextView.setText(webViewDefaultUserAgentString);
593                     break;
594
595                 case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
596                     // Display the custom user agent.
597                     userAgentTextView.setText(defaultCustomUserAgentString);
598                     break;
599
600                 default:
601                     // Get the user agent string from the user agent data array.
602                     userAgentTextView.setText(userAgentDataArray[defaultUserAgentArrayPosition]);
603             }
604         } else if (userAgentArrayPosition == MainWebViewActivity.UNRECOGNIZED_USER_AGENT || currentUserAgentName.equals(getString(R.string.custom_user_agent))) {
605             // A custom user agent is stored in the current user agent name.  The second check is necessary in case the user did not change the default custom text.
606             // Set the user agent spinner to `Custom user agent`.
607             userAgentSpinner.setSelection(MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT);
608
609             // Hide the user agent TextView.
610             userAgentTextView.setVisibility(View.GONE);
611
612             // Show the custom user agent EditText and set the current user agent name as the text.
613             customUserAgentEditText.setVisibility(View.VISIBLE);
614             customUserAgentEditText.setText(currentUserAgentName);
615         } else {  // The user agent name contains one of the canonical user agents.
616             // 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.
617             userAgentSpinner.setSelection(userAgentArrayPosition + 1);
618
619             // Show the user agent TextView.
620             userAgentTextView.setVisibility(View.VISIBLE);
621
622             // Hide the custom user agent EditText.
623             customUserAgentEditText.setVisibility(View.GONE);
624
625             // Set the user agent text.
626             if (userAgentArrayPosition == MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT) {  // The WebView default user agent is selected.
627                 // Display the WebView default user agent.
628                 userAgentTextView.setText(webViewDefaultUserAgentString);
629             } else {  // A user agent besides the default is selected.
630                 // 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.
631                 userAgentTextView.setText(userAgentDataArray[userAgentArrayPosition + 1]);
632             }
633         }
634
635         // Open the user agent spinner when the text view is clicked.
636         userAgentTextView.setOnClickListener((View v) -> {
637             // Open the user agent spinner.
638             userAgentSpinner.performClick();
639         });
640
641         // Display the font size settings.
642         if (fontSizeInt == 0) {  // `0` is the code for system default font size.
643             // Set the font size to the system default
644             fontSizeSpinner.setSelection(0);
645
646             // Show the default font size text view.
647             defaultFontSizeTextView.setVisibility(View.VISIBLE);
648
649             // Hide the custom font size edit text.
650             customFontSizeEditText.setVisibility(View.GONE);
651
652             // 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.
653             customFontSizeEditText.setText(defaultFontSizeString);
654         } else {  // A custom font size is selected.
655             // Set the spinner to the custom font size.
656             fontSizeSpinner.setSelection(1);
657
658             // Hide the default font size text view.
659             defaultFontSizeTextView.setVisibility(View.GONE);
660
661             // Show the custom font size edit text.
662             customFontSizeEditText.setVisibility(View.GONE);
663
664             // Set the custom font size.
665             customFontSizeEditText.setText(String.valueOf(fontSizeInt));
666         }
667
668         // Initialize the default font size percentage string.
669         String defaultFontSizePercentageString = defaultFontSizeString + "%";
670
671         // Set the default font size text in the text view.
672         defaultFontSizeTextView.setText(defaultFontSizePercentageString);
673
674         // Open the font size spinner when the text view is clicked.
675         defaultFontSizeTextView.setOnClickListener((View v) -> {
676             // Open the user agent spinner.
677             fontSizeSpinner.performClick();
678         });
679
680         // Select the swipe to refresh selection in the spinner.
681         swipeToRefreshSpinner.setSelection(swipeToRefreshInt);
682
683         // Set the swipe to refresh text.
684         if (defaultSwipeToRefresh) {
685             swipeToRefreshTextView.setText(swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
686         } else {
687             swipeToRefreshTextView.setText(swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
688         }
689
690         // Set the swipe to refresh icon and text view settings.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
691         // Doing this makes no sense until it can also be done with the preferences.
692         switch (swipeToRefreshInt) {
693             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
694                 if (defaultSwipeToRefresh) {  // Swipe to refresh is enabled by default.
695                     // Set the icon.
696                     swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled, null));
697                 } else {
698                     // Set the icon.
699                     swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled, null));
700                 }
701
702                 // Show the swipe to refresh text view.
703                 swipeToRefreshTextView.setVisibility(View.VISIBLE);
704                 break;
705
706             case DomainsDatabaseHelper.ENABLED:
707                 // Set the icon.
708                 swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled, null));
709
710                 // Hide the swipe to refresh text view.
711                 swipeToRefreshTextView.setVisibility(View.GONE);
712                 break;
713
714             case DomainsDatabaseHelper.DISABLED:
715                 // Set the icon.
716                 swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled, null));
717
718                 // Hide the swipe to refresh text view.
719                 swipeToRefreshTextView.setVisibility(View.GONE);
720                 break;
721         }
722
723         // Open the swipe to refresh spinner when the text view is clicked.
724         swipeToRefreshTextView.setOnClickListener((View v) -> {
725             // Open the swipe to refresh spinner.
726             swipeToRefreshSpinner.performClick();
727         });
728
729         // Get the WebView theme string arrays.
730         String[] webViewThemeStringArray = resources.getStringArray(R.array.webview_theme_array);
731         String[] webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values);
732
733         // Define an app WebView theme entry number.
734         int appWebViewThemeEntryNumber;
735
736         // 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.
737         if (defaultWebViewTheme.equals(webViewThemeEntryValuesStringArray[1])) {  // The light theme is selected.
738             // Store the default WebView theme entry number.
739             appWebViewThemeEntryNumber = 1;
740         } else if (defaultWebViewTheme.equals(webViewThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
741             // Store the default WebView theme entry number.
742             appWebViewThemeEntryNumber = 2;
743         } else {  // The system default theme is selected.
744             // Store the default WebView theme entry number.
745             appWebViewThemeEntryNumber = 0;
746         }
747
748         // Select the WebView theme in the spinner.
749         webViewThemeSpinner.setSelection(webViewThemeInt);
750
751         // Set the WebView theme text.
752         if (appWebViewThemeEntryNumber == DomainsDatabaseHelper.SYSTEM_DEFAULT) {  // The app WebView theme is system default.
753             // Set the text according to the current UI theme.
754             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
755                 webViewThemeTextView.setText(webViewThemeStringArray[DomainsDatabaseHelper.LIGHT_THEME]);
756             } else {
757                 webViewThemeTextView.setText(webViewThemeStringArray[DomainsDatabaseHelper.DARK_THEME]);
758             }
759         } else {  // The app WebView theme is not system default.
760             // Set the text according to the app WebView theme.
761             webViewThemeTextView.setText(webViewThemeStringArray[appWebViewThemeEntryNumber]);
762         }
763
764         // 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.
765         // Doing this makes no sense until it can also be done with the preferences.
766         switch (webViewThemeInt) {
767             case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // The domain WebView theme is system default.
768                 // Set the icon according to the app WebView theme.
769                 switch (appWebViewThemeEntryNumber) {
770                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // The default WebView theme is system default.
771                         // Set the icon according to the app theme.
772                         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
773                             // Set the light theme icon.
774                             webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme, null));
775                         } else {
776                             // Set the dark theme icon.
777                             webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme, null));
778                         }
779                         break;
780
781                     case DomainsDatabaseHelper.LIGHT_THEME:  // the default WebView theme is light.
782                         // Set the icon.
783                         webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme, null));
784                         break;
785
786                     case DomainsDatabaseHelper.DARK_THEME:  // the default WebView theme is dark.
787                         // Set the icon.
788                         webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme, null));
789                         break;
790                 }
791
792                 // Show the WebView theme text view.
793                 webViewThemeTextView.setVisibility(View.VISIBLE);
794                 break;
795
796             case DomainsDatabaseHelper.LIGHT_THEME:  // The domain WebView theme is light.
797                 // Set the icon.
798                 webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme, null));
799
800                 // Hide the WebView theme text view.
801                 webViewThemeTextView.setVisibility(View.GONE);
802                 break;
803
804             case DomainsDatabaseHelper.DARK_THEME:  // The domain WebView theme is dark.
805                 // Set the icon.
806                 webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme, null));
807
808                 // Hide the WebView theme text view.
809                 webViewThemeTextView.setVisibility(View.GONE);
810                 break;
811         }
812
813         // Open the WebView theme spinner when the text view is clicked.
814         webViewThemeTextView.setOnClickListener((View v) -> {
815             // Open the WebView theme spinner.
816             webViewThemeSpinner.performClick();
817         });
818
819         // Select the wide viewport in the spinner.
820         wideViewportSpinner.setSelection(wideViewportInt);
821
822         // Set the default wide viewport text.
823         if (defaultWideViewport) {
824             wideViewportTextView.setText(wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
825         } else {
826             wideViewportTextView.setText(wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
827         }
828
829         // 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.
830         // Doing this makes no sense until it can also be done with the preferences.
831         switch (wideViewportInt) {
832             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
833                 // Set the icon.
834                 if (defaultWideViewport) {  // Wide viewport enabled by default.
835                     wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled, null));
836                 } else {  // Wide viewport disabled by default.
837                     wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled, null));
838                 }
839
840                 // Show the wide viewport text view.
841                 wideViewportTextView.setVisibility(View.VISIBLE);
842                 break;
843
844             case DomainsDatabaseHelper.ENABLED:
845                 // Set the icon according to the theme.
846                 wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled, null));
847
848                 // Hide the wide viewport text view.
849                 wideViewportTextView.setVisibility(View.GONE);
850                 break;
851
852             case DomainsDatabaseHelper.DISABLED:
853                 // Set the icon.
854                 wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled, null));
855
856                 // Hide the wide viewport text view.
857                 wideViewportTextView.setVisibility(View.GONE);
858                 break;
859         }
860
861         // Open the wide viewport spinner when the text view is clicked.
862         wideViewportTextView.setOnClickListener((View view) -> {
863             // Open the wide viewport spinner.
864             wideViewportSpinner.performClick();
865         });
866
867         // Display the website images mode in the spinner.
868         displayWebpageImagesSpinner.setSelection(displayImagesInt);
869
870         // Set the default display images text.
871         if (defaultDisplayWebpageImages) {
872             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
873         } else {
874             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
875         }
876
877         // 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.
878         // Doing this makes no sense until it can also be done with the preferences.
879         switch (displayImagesInt) {
880             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
881                 if (defaultDisplayWebpageImages) {  // Display webpage images enabled by default.
882                     // Set the icon.
883                     displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled, null));
884                 } else {  // Display webpage images disabled by default.
885                     // Set the icon.
886                     displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled, null));
887                 }
888
889                 // Show the display images text view.
890                 displayImagesTextView.setVisibility(View.VISIBLE);
891                 break;
892
893             case DomainsDatabaseHelper.ENABLED:
894                 // Set the icon.
895                 displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled, null));
896
897                 // Hide the display images text view.
898                 displayImagesTextView.setVisibility(View.GONE);
899                 break;
900
901             case DomainsDatabaseHelper.DISABLED:
902                 // Set the icon.
903                 displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled, null));
904
905                 // Hide the display images text view.
906                 displayImagesTextView.setVisibility(View.GONE);
907                 break;
908         }
909
910         // Open the display images spinner when the text view is clicked.
911         displayImagesTextView.setOnClickListener((View view) -> {
912             // Open the user agent spinner.
913             displayWebpageImagesSpinner.performClick();
914         });
915         
916         // Set the pinned SSL certificate icon.
917         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.
918             // Doing this makes no sense until it can also be done with the preferences.
919             // Check the switch.
920             pinnedSslCertificateSwitch.setChecked(true);
921
922             // Set the icon.
923             pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled, null));
924         } else {  // Pinned SSL certificate is disabled.
925             // Uncheck the switch.
926             pinnedSslCertificateSwitch.setChecked(false);
927
928             // Set the icon.
929             pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled, null));
930         }
931
932         // Store the current date.
933         Date currentDate = Calendar.getInstance().getTime();
934
935         // Setup the string builders to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
936         savedSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
937         savedSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
938         savedSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
939         savedSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
940         savedSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
941
942         // Check the certificate Common Name against the domain name.
943         boolean savedSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslIssuedToCNameString);
944
945         // Format the issued to Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
946         if (savedSslCommonNameMatchesDomainName) {
947             savedSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
948         } else {
949             savedSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), savedSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
950         }
951
952         //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
953         if ((savedSslStartDate != null) && savedSslStartDate.after(currentDate)) {  // The certificate start date is in the future.
954             savedSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), savedSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
955         } else {  // The certificate start date is in the past.
956             savedSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), savedSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
957         }
958
959         // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
960         if ((savedSslEndDate != null) && savedSslEndDate.before(currentDate)) {  // The certificate end date is in the past.
961             savedSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), savedSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
962         } else {  // The certificate end date is in the future.
963             savedSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), savedSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
964         }
965
966         // Display the saved website SSL certificate strings.
967         savedSslIssuedToCNameTextView.setText(savedSslIssuedToCNameStringBuilder);
968         savedSslIssuedToONameTextView.setText(savedSslIssuedToONameStringBuilder);
969         savedSslIssuedToUNameTextView.setText(savedSslIssuedToUNameStringBuilder);
970         savedSslIssuedByCNameTextView.setText(savedSslIssuedByCNameStringBuilder);
971         savedSslIssuedByONameTextView.setText(savedSslIssuedByONameStringBuilder);
972         savedSslIssuedByUNameTextView.setText(savedSslIssuedByUNameStringBuilder);
973         savedSslStartDateTextView.setText(savedSslStartDateStringBuilder);
974         savedSslEndDateTextView.setText(savedSslEndDateStringBuilder);
975
976         // Populate the current website SSL certificate if there is one.
977         if (DomainsActivity.sslIssuedToCName != null) {
978             // Get dates from the raw long values.
979             Date currentSslStartDate = new Date(DomainsActivity.sslStartDateLong);
980             Date currentSslEndDate = new Date(DomainsActivity.sslEndDateLong);
981
982             // Create a spannable string builder for each text view that needs multiple colors of text.
983             SpannableStringBuilder currentSslIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName);
984             SpannableStringBuilder currentSslIssuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedToOName);
985             SpannableStringBuilder currentSslIssuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedToUName);
986             SpannableStringBuilder currentSslIssuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedByCName);
987             SpannableStringBuilder currentSslIssuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedByOName);
988             SpannableStringBuilder currentSslIssuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedByUName);
989             SpannableStringBuilder currentSslStartDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
990                     .format(currentSslStartDate));
991             SpannableStringBuilder currentSslEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
992                     .format(currentSslEndDate));
993
994             // Setup the string builders to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
995             currentSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentSslIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
996             currentSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentSslIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
997             currentSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
998             currentSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentSslIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
999             currentSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentSslIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1000
1001             // Check the certificate Common Name against the domain name.
1002             boolean currentSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, DomainsActivity.sslIssuedToCName);
1003
1004             // Format the issued to Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1005             if (currentSslCommonNameMatchesDomainName) {
1006                 currentSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1007             } else {
1008                 currentSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), currentSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1009             }
1010
1011             //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1012             if (currentSslStartDate.after(currentDate)) {  // The certificate start date is in the future.
1013                 currentSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), currentSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1014             } else {  // The certificate start date is in the past.
1015                 currentSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), currentSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1016             }
1017
1018             // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
1019             if (currentSslEndDate.before(currentDate)) {  // The certificate end date is in the past.
1020                 currentSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), currentSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1021             } else {  // The certificate end date is in the future.
1022                 currentSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), currentSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1023             }
1024
1025             // Display the current website SSL certificate strings.
1026             currentSslIssuedToCNameTextView.setText(currentSslIssuedToCNameStringBuilder);
1027             currentSslIssuedToONameTextView.setText(currentSslIssuedToONameStringBuilder);
1028             currentSslIssuedToUNameTextView.setText(currentSslIssuedToUNameStringBuilder);
1029             currentSslIssuedByCNameTextView.setText(currentSslIssuedByCNameStringBuilder);
1030             currentSslIssuedByONameTextView.setText(currentSslIssuedByONameStringBuilder);
1031             currentSslIssuedByUNameTextView.setText(currentSslIssuedByUNameStringBuilder);
1032             currentSslStartDateTextView.setText(currentSslStartDateStringBuilder);
1033             currentSslEndDateTextView.setText(currentSslEndDateStringBuilder);
1034         }
1035
1036         // Set the initial display status of the SSL certificates card views.
1037         if (pinnedSslCertificateSwitch.isChecked()) {  // An SSL certificate is pinned.
1038             // Set the visibility of the saved SSL certificate.
1039             if (savedSslIssuedToCNameString == null) {
1040                 savedSslCardView.setVisibility(View.GONE);
1041             } else {
1042                 savedSslCardView.setVisibility(View.VISIBLE);
1043             }
1044
1045             // Set the visibility of the current website SSL certificate.
1046             if (DomainsActivity.sslIssuedToCName == null) {  // There is no current SSL certificate.
1047                 // Hide the SSL certificate.
1048                 currentSslCardView.setVisibility(View.GONE);
1049
1050                 // Show the instruction.
1051                 noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
1052             } else {  // There is a current SSL certificate.
1053                 // Show the SSL certificate.
1054                 currentSslCardView.setVisibility(View.VISIBLE);
1055
1056                 // Hide the instruction.
1057                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1058             }
1059
1060             // Set the status of the radio buttons and the card view backgrounds.
1061             if (savedSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
1062                 // Check the saved SSL certificate radio button.
1063                 savedSslCertificateRadioButton.setChecked(true);
1064
1065                 // Uncheck the current website SSL certificate radio button.
1066                 currentWebsiteCertificateRadioButton.setChecked(false);
1067
1068                 // Darken the background of the current website SSL certificate linear layout according to the theme.
1069                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1070                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1071                 } else {
1072                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1073                 }
1074             } else if (currentSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1075                 // Check the current website SSL certificate radio button.
1076                 currentWebsiteCertificateRadioButton.setChecked(true);
1077
1078                 // Uncheck the saved SSL certificate radio button.
1079                 savedSslCertificateRadioButton.setChecked(false);
1080             } else {  // Neither SSL certificate is visible.
1081                 // Uncheck both radio buttons.
1082                 savedSslCertificateRadioButton.setChecked(false);
1083                 currentWebsiteCertificateRadioButton.setChecked(false);
1084             }
1085         } else {  // An SSL certificate is not pinned.
1086             // Hide the SSl certificates and instructions.
1087             savedSslCardView.setVisibility(View.GONE);
1088             currentSslCardView.setVisibility(View.GONE);
1089             noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1090
1091             // Uncheck the radio buttons.
1092             savedSslCertificateRadioButton.setChecked(false);
1093             currentWebsiteCertificateRadioButton.setChecked(false);
1094         }
1095
1096         // Set the pinned IP addresses icon.
1097         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.
1098             // Doing this makes no sense until it can also be done with the preferences.
1099             // Check the switch.
1100             pinnedIpAddressesSwitch.setChecked(true);
1101
1102             // Set the icon.
1103             pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled, null));
1104         } else {  // Pinned IP Addresses is disabled.
1105             // Uncheck the switch.
1106             pinnedIpAddressesSwitch.setChecked(false);
1107
1108             // Set the icon.
1109             pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled, null));
1110         }
1111
1112         // Populate the saved and current IP addresses.
1113         savedIpAddressesTextView.setText(savedIpAddresses);
1114         currentIpAddressesTextView.setText(DomainsActivity.currentIpAddresses);
1115
1116         // Set the initial display status of the IP addresses card views.
1117         if (pinnedIpAddressesSwitch.isChecked()) {  // IP addresses are pinned.
1118             // Set the visibility of the saved IP addresses.
1119             if (savedIpAddresses == null) {  // There are no saved IP addresses.
1120                 savedIpAddressesCardView.setVisibility(View.GONE);
1121             } else {  // There are saved IP addresses.
1122                 savedIpAddressesCardView.setVisibility(View.VISIBLE);
1123             }
1124
1125             // Set the visibility of the current IP addresses.
1126             currentIpAddressesCardView.setVisibility(View.VISIBLE);
1127
1128             // Set the status of the radio buttons and the card view backgrounds.
1129             if (savedIpAddressesCardView.getVisibility() == View.VISIBLE) {  // The saved IP addresses are displayed.
1130                 // Check the saved IP addresses radio button.
1131                 savedIpAddressesRadioButton.setChecked(true);
1132
1133                 // Uncheck the current IP addresses radio button.
1134                 currentIpAddressesRadioButton.setChecked(false);
1135
1136                 // Darken the background of the current IP addresses linear layout according to the theme.
1137                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1138                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1139                 } else {
1140                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1141                 }
1142             } else {  // The saved IP addresses are hidden.
1143                 // Check the current IP addresses radio button.
1144                 currentIpAddressesRadioButton.setChecked(true);
1145
1146                 // Uncheck the saved IP addresses radio button.
1147                 savedIpAddressesRadioButton.setChecked(false);
1148             }
1149         } else {  // IP addresses are not pinned.
1150             // Hide the IP addresses card views.
1151             savedIpAddressesCardView.setVisibility(View.GONE);
1152             currentIpAddressesCardView.setVisibility(View.GONE);
1153
1154             // Uncheck the radio buttons.
1155             savedIpAddressesRadioButton.setChecked(false);
1156             currentIpAddressesRadioButton.setChecked(false);
1157         }
1158
1159
1160         // Set the JavaScript switch listener.
1161         javaScriptSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1162             if (isChecked) {  // JavaScript is enabled.
1163                 // Update the JavaScript icon.
1164                 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null));
1165
1166                 // Enable the DOM storage `Switch`.
1167                 domStorageSwitch.setEnabled(true);
1168
1169                 // Update the DOM storage icon.
1170                 if (domStorageSwitch.isChecked()) {  // DOM storage is enabled.
1171                     domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_enabled, null));
1172                 } else {  // DOM storage is disabled.
1173                     // Set the icon.
1174                     domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled, null));
1175                 }
1176             } else {  // JavaScript is disabled.
1177                 // Update the JavaScript icon.
1178                 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null));
1179
1180                 // Disable the DOM storage switch.
1181                 domStorageSwitch.setEnabled(false);
1182
1183                 // Set the DOM storage icon.
1184                 domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_ghosted, null));
1185             }
1186         });
1187
1188         // Set the cookies switch listener.
1189         cookiesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1190             // Update the cookies icon.
1191             if (isChecked) {
1192                 cookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_enabled, null));
1193             } else {
1194                 cookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled, null));
1195             }
1196         });
1197
1198         // Set the DOM Storage switch listener.
1199         domStorageSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1200             // Update the icon.
1201             if (isChecked) {
1202                 domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_enabled, null));
1203             } else {
1204                 // Set the icon.
1205                 domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled, null));
1206             }
1207         });
1208
1209         // Set the form data switch listener.  It can be removed once the minimum API >= 26.
1210         if (Build.VERSION.SDK_INT < 26) {
1211             formDataSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1212                 // Update the icon.
1213                 if (isChecked) {
1214                     formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_enabled, null));
1215                 } else {
1216                     // Set the icon.
1217                     formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_disabled, null));
1218                 }
1219             });
1220         }
1221
1222         // Set the EasyList switch listener.
1223         easyListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1224             // Update the icon.
1225             if (isChecked) {
1226                 easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled, null));
1227             } else {
1228                 easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled, null));
1229             }
1230         });
1231
1232         // Set the EasyPrivacy switch listener.
1233         easyPrivacySwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1234             // Update the icon.
1235             if (isChecked) {  // EasyPrivacy is on.
1236                 // Set the icon.
1237                 easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled, null));
1238             } else {
1239                 easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled, null));
1240             }
1241         });
1242
1243         // Set the Fanboy's Annoyance List switch listener.
1244         fanboysAnnoyanceListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1245             // Update the icon and Fanboy's Social Blocking List.
1246             if (isChecked) {  // Fanboy's Annoyance List is on.
1247                 // Set the icon.
1248                 fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled, null));
1249
1250                 // Disable the Fanboy's Social Blocking List switch.
1251                 fanboysSocialBlockingListSwitch.setEnabled(false);
1252
1253                 // Update the Fanboy's Social Blocking List icon.
1254                 fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_ghosted, null));
1255             } else {  // Fanboy's Annoyance List is off.
1256                 // Set the icon.
1257                 fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled, null));
1258
1259                 // Enable the Fanboy's Social Blocking List switch.
1260                 fanboysSocialBlockingListSwitch.setEnabled(true);
1261
1262                 // Update the Fanboy's Social Blocking List icon.
1263                 if (fanboysSocialBlockingListSwitch.isChecked()) {
1264                     fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled, null));
1265                 } else {
1266                     fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled, null));
1267                 }
1268             }
1269         });
1270
1271         // Set the Fanboy's Social Blocking List switch listener.
1272         fanboysSocialBlockingListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1273             // Update the icon.
1274             if (isChecked) {
1275                 fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled, null));
1276             } else {
1277                 fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled, null));
1278             }
1279         });
1280
1281         // Set the UltraList switch listener.
1282         ultraListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1283             // Update the icon.
1284             if (isChecked) {
1285                 ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled, null));
1286             } else {
1287                 ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled, null));
1288             }
1289         });
1290
1291         // Set the UltraPrivacy switch listener.
1292         ultraPrivacySwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1293             // Update the icon.
1294             if (isChecked) {
1295                 ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled, null));
1296             } else {
1297                 ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled, null));
1298             }
1299         });
1300
1301         // Set the block all third-party requests switch listener.
1302         blockAllThirdPartyRequestsSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1303             // Update the icon.
1304             if (isChecked) {
1305                 blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_enabled, null));
1306             } else {
1307                 blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_disabled, null));
1308             }
1309         });
1310
1311         // Set the user agent spinner listener.
1312         userAgentSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1313             @Override
1314             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1315                 // Set the new user agent.
1316                 switch (position) {
1317                     case MainWebViewActivity.DOMAINS_SYSTEM_DEFAULT_USER_AGENT:
1318                         // Show the user agent TextView.
1319                         userAgentTextView.setVisibility(View.VISIBLE);
1320
1321                         // Hide the custom user agent EditText.
1322                         customUserAgentEditText.setVisibility(View.GONE);
1323
1324                         // Set the user text.
1325                         switch (defaultUserAgentArrayPosition) {
1326                             case MainWebViewActivity.UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
1327                                 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
1328                                 userAgentTextView.setText(defaultUserAgentName);
1329                                 break;
1330
1331                             case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
1332                                 // Display the `WebView` default user agent.
1333                                 userAgentTextView.setText(webViewDefaultUserAgentString);
1334                                 break;
1335
1336                             case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
1337                                 // Display the custom user agent.
1338                                 userAgentTextView.setText(defaultCustomUserAgentString);
1339                                 break;
1340
1341                             default:
1342                                 // Get the user agent string from the user agent data array.
1343                                 userAgentTextView.setText(userAgentDataArray[defaultUserAgentArrayPosition]);
1344                         }
1345                         break;
1346
1347                     case MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT:
1348                         // Show the user agent TextView and set the text.
1349                         userAgentTextView.setVisibility(View.VISIBLE);
1350                         userAgentTextView.setText(webViewDefaultUserAgentString);
1351
1352                         // Hide the custom user agent EditTex.
1353                         customUserAgentEditText.setVisibility(View.GONE);
1354                         break;
1355
1356                     case MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT:
1357                         // Hide the user agent TextView.
1358                         userAgentTextView.setVisibility(View.GONE);
1359
1360                         // Show the custom user agent EditText and set the current user agent name as the text.
1361                         customUserAgentEditText.setVisibility(View.VISIBLE);
1362                         customUserAgentEditText.setText(currentUserAgentName);
1363                         break;
1364
1365                     default:
1366                         // 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.
1367                         userAgentTextView.setVisibility(View.VISIBLE);
1368                         userAgentTextView.setText(userAgentDataArray[position - 1]);
1369
1370                         // Hide `customUserAgentEditText`.
1371                         customUserAgentEditText.setVisibility(View.GONE);
1372                 }
1373             }
1374
1375             @Override
1376             public void onNothingSelected(AdapterView<?> parent) {
1377                 // Do nothing.
1378             }
1379         });
1380
1381         // Set the font size spinner listener.
1382         fontSizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1383             @Override
1384             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1385                 // Update the font size display options.
1386                 if (position == 0) {  // The system default font size has been selected.
1387                     // Show the default font size text view.
1388                     defaultFontSizeTextView.setVisibility(View.VISIBLE);
1389
1390                     // Hide the custom font size edit text.
1391                     customFontSizeEditText.setVisibility(View.GONE);
1392                 } else {  // A custom font size has been selected.
1393                     // Hide the default font size text view.
1394                     defaultFontSizeTextView.setVisibility(View.GONE);
1395
1396                     // Show the custom font size edit text.
1397                     customFontSizeEditText.setVisibility(View.VISIBLE);
1398                 }
1399             }
1400
1401             @Override
1402             public void onNothingSelected(AdapterView<?> parent) {
1403                 // Do nothing.
1404             }
1405         });
1406
1407         // Set the swipe to refresh spinner listener.
1408         swipeToRefreshSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1409             @Override
1410             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1411                 // Update the icon and the visibility of the night mode text view.  Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
1412                 // Doing this makes no sense until it can also be done with the preferences.
1413                 switch (position) {
1414                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1415                         if (defaultSwipeToRefresh) {
1416                             // Set the icon.
1417                             swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled, null));
1418                         } else {
1419                             // Set the icon.
1420                             swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled, null));
1421                         }
1422
1423                         // Show the swipe to refresh text view.
1424                         swipeToRefreshTextView.setVisibility(View.VISIBLE);
1425                         break;
1426
1427                     case DomainsDatabaseHelper.ENABLED:
1428                         // Set the icon.
1429                         swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled, null));
1430
1431                         // Hide the swipe to refresh text view.
1432                         swipeToRefreshTextView.setVisibility(View.GONE);
1433                         break;
1434
1435                     case DomainsDatabaseHelper.DISABLED:
1436                         // Set the icon.
1437                         swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled, null));
1438
1439                         // Hide the swipe to refresh text view.
1440                         swipeToRefreshTextView.setVisibility(View.GONE);
1441                 }
1442             }
1443
1444             @Override
1445             public void onNothingSelected(AdapterView<?> parent) {
1446                 // Do nothing.
1447             }
1448         });
1449
1450         // Set the WebView theme spinner listener.
1451         webViewThemeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1452             @Override
1453             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1454                 // 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.
1455                 // Doing this makes no sense until it can also be done with the preferences.
1456                 switch (position) {
1457                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // the domain WebView theme is system default.
1458                         // Set the icon according to the app WebView theme.
1459                         switch (appWebViewThemeEntryNumber) {
1460                             case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // The default WebView theme is system default.
1461                                 // Set the icon according to the app theme.
1462                                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
1463                                     // Set the light theme icon.
1464                                     webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme, null));
1465                                 } else {
1466                                     // Set the dark theme icon.
1467                                     webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme, null));
1468                                 }
1469                                 break;
1470
1471                             case DomainsDatabaseHelper.LIGHT_THEME:  // The default WebView theme is light.
1472                                 // Set the icon.
1473                                 webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme, null));
1474                                 break;
1475
1476                             case DomainsDatabaseHelper.DARK_THEME:  // The default WebView theme is dark.
1477                                 // Set the icon.
1478                                 webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme, null));
1479                                 break;
1480                         }
1481
1482                         // Show the WebView theme text view.
1483                         webViewThemeTextView.setVisibility(View.VISIBLE);
1484                         break;
1485
1486                     case DomainsDatabaseHelper.LIGHT_THEME:  // The domain WebView theme is light.
1487                         // Set the icon.
1488                         webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme, null));
1489
1490                         // Hide the WebView theme text view.
1491                         webViewThemeTextView.setVisibility(View.GONE);
1492                         break;
1493
1494                     case DomainsDatabaseHelper.DARK_THEME:  // The domain WebView theme is dark.
1495                         // Set the icon.
1496                         webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme, null));
1497
1498                         // Hide the WebView theme text view.
1499                         webViewThemeTextView.setVisibility(View.GONE);
1500                         break;
1501                 }
1502             }
1503
1504             @Override
1505             public void onNothingSelected(AdapterView<?> parent) {
1506                 // Do nothing.
1507             }
1508         });
1509
1510         // Set the wide viewport spinner listener.
1511         wideViewportSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1512             @Override
1513             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1514                 // Update the icon and the visibility of the wide viewport text view.
1515                 switch (position) {
1516                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1517                         // Set the icon.
1518                         if (defaultWideViewport) {  // Wide viewport is enabled by default.
1519                             wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled, null));
1520                         } else {  // Wide viewport is disabled by default.
1521                             wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled, null));
1522                         }
1523
1524                         // Show the wide viewport text view.
1525                         wideViewportTextView.setVisibility(View.VISIBLE);
1526                         break;
1527
1528                     case DomainsDatabaseHelper.ENABLED:
1529                         // Set the icon according to the theme.
1530                         wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled, null));
1531
1532                         // Hide the wide viewport text view.
1533                         wideViewportTextView.setVisibility(View.GONE);
1534                         break;
1535
1536                     case DomainsDatabaseHelper.DISABLED:
1537                         // Set the icon.
1538                         wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled, null));
1539
1540                         // Hid ethe wide viewport text view.
1541                         wideViewportTextView.setVisibility(View.GONE);
1542                         break;
1543                 }
1544             }
1545
1546             @Override
1547             public void onNothingSelected(AdapterView<?> parent) {
1548                 // Do nothing.
1549             }
1550         });
1551
1552         // Set the display webpage images spinner listener.
1553         displayWebpageImagesSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1554             @Override
1555             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1556                 // Update the icon and the visibility of the display images text view.
1557                 switch (position) {
1558                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1559                         if (defaultDisplayWebpageImages) {  // Display webpage images is enabled by default.
1560                             // Set the icon.
1561                             displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled, null));
1562                         } else {  // Display webpage images is disabled by default.
1563                             // Set the icon.
1564                             displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled, null));
1565                         }
1566
1567                         // Show the display images text view.
1568                         displayImagesTextView.setVisibility(View.VISIBLE);
1569                         break;
1570
1571                     case DomainsDatabaseHelper.ENABLED:
1572                         // Set the icon.
1573                         displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled, null));
1574
1575                         // Hide the display images text view.
1576                         displayImagesTextView.setVisibility(View.GONE);
1577                         break;
1578
1579                     case DomainsDatabaseHelper.DISABLED:
1580                         // Set the icon.
1581                         displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled, null));
1582
1583                         // Hide the display images text view.
1584                         displayImagesTextView.setVisibility(View.GONE);
1585                         break;
1586                 }
1587             }
1588
1589             @Override
1590             public void onNothingSelected(AdapterView<?> parent) {
1591                 // Do nothing.
1592             }
1593         });
1594         
1595         // Set the pinned SSL certificate switch listener.
1596         pinnedSslCertificateSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1597             // Update the icon.
1598             if (isChecked) {  // SSL certificate pinning is enabled.
1599                 // Set the icon.
1600                 pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled, null));
1601
1602                 // Update the visibility of the saved SSL certificate.
1603                 if (savedSslIssuedToCNameString == null) {
1604                     savedSslCardView.setVisibility(View.GONE);
1605                 } else {
1606                     savedSslCardView.setVisibility(View.VISIBLE);
1607                 }
1608
1609                 // Update the visibility of the current website SSL certificate.
1610                 if (DomainsActivity.sslIssuedToCName == null) {
1611                     // Hide the SSL certificate.
1612                     currentSslCardView.setVisibility(View.GONE);
1613
1614                     // Show the instruction.
1615                     noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
1616                 } else {
1617                     // Show the SSL certificate.
1618                     currentSslCardView.setVisibility(View.VISIBLE);
1619
1620                     // Hide the instruction.
1621                     noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1622                 }
1623
1624                 // Set the status of the radio buttons.
1625                 if (savedSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
1626                     // Check the saved SSL certificate radio button.
1627                     savedSslCertificateRadioButton.setChecked(true);
1628
1629                     // Uncheck the current website SSL certificate radio button.
1630                     currentWebsiteCertificateRadioButton.setChecked(false);
1631
1632                     // Set the background of the saved SSL certificate linear layout to be transparent.
1633                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1634
1635                     // Darken the background of the current website SSL certificate linear layout according to the theme.
1636                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1637                         currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1638                     } else {
1639                         currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1640                     }
1641
1642                     // Scroll to the current website SSL certificate card.
1643                     savedSslCardView.getParent().requestChildFocus(savedSslCardView, savedSslCardView);
1644                 } else if (currentSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1645                     // Check the current website SSL certificate radio button.
1646                     currentWebsiteCertificateRadioButton.setChecked(true);
1647
1648                     // Uncheck the saved SSL certificate radio button.
1649                     savedSslCertificateRadioButton.setChecked(false);
1650
1651                     // Set the background of the current website SSL certificate linear layout to be transparent.
1652                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1653
1654                     // Darken the background of the saved SSL certificate linear layout according to the theme.
1655                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1656                         savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1657                     } else {
1658                         savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1659                     }
1660
1661                     // Scroll to the current website SSL certificate card.
1662                     currentSslCardView.getParent().requestChildFocus(currentSslCardView, currentSslCardView);
1663                 } else {  // Neither SSL certificate is visible.
1664                     // Uncheck both radio buttons.
1665                     savedSslCertificateRadioButton.setChecked(false);
1666                     currentWebsiteCertificateRadioButton.setChecked(false);
1667
1668                     // Scroll to the current website SSL certificate card.
1669                     noCurrentWebsiteCertificateTextView.getParent().requestChildFocus(noCurrentWebsiteCertificateTextView, noCurrentWebsiteCertificateTextView);
1670                 }
1671             } else {  // SSL certificate pinning is disabled.
1672                 // Set the icon.
1673                 pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled, null));
1674
1675                 // Hide the SSl certificates and instructions.
1676                 savedSslCardView.setVisibility(View.GONE);
1677                 currentSslCardView.setVisibility(View.GONE);
1678                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1679
1680                 // Uncheck the radio buttons.
1681                 savedSslCertificateRadioButton.setChecked(false);
1682                 currentWebsiteCertificateRadioButton.setChecked(false);
1683             }
1684         });
1685
1686         savedSslCardView.setOnClickListener((View view) -> {
1687             // Check the saved SSL certificate radio button.
1688             savedSslCertificateRadioButton.setChecked(true);
1689
1690             // Uncheck the current website SSL certificate radio button.
1691             currentWebsiteCertificateRadioButton.setChecked(false);
1692
1693             // Set the background of the saved SSL certificate linear layout to be transparent.
1694             savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1695
1696             // Darken the background of the current website SSL certificate linear layout according to the theme.
1697             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1698                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1699             } else {
1700                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1701             }
1702         });
1703
1704         savedSslCertificateRadioButton.setOnClickListener((View view) -> {
1705             // Check the saved SSL certificate radio button.
1706             savedSslCertificateRadioButton.setChecked(true);
1707
1708             // Uncheck the current website SSL certificate radio button.
1709             currentWebsiteCertificateRadioButton.setChecked(false);
1710
1711             // Set the background of the saved SSL certificate linear layout to be transparent.
1712             savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1713
1714             // Darken the background of the current website SSL certificate linear layout according to the theme.
1715             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1716                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1717             } else {
1718                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1719             }
1720         });
1721
1722         currentSslCardView.setOnClickListener((View view) -> {
1723             // Check the current website SSL certificate radio button.
1724             currentWebsiteCertificateRadioButton.setChecked(true);
1725
1726             // Uncheck the saved SSL certificate radio button.
1727             savedSslCertificateRadioButton.setChecked(false);
1728
1729             // Set the background of the current website SSL certificate linear layout to be transparent.
1730             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1731
1732             // Darken the background of the saved SSL certificate linear layout according to the theme.
1733             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1734                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1735             } else {
1736                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1737             }
1738         });
1739
1740         currentWebsiteCertificateRadioButton.setOnClickListener((View view) -> {
1741             // Check the current website SSL certificate radio button.
1742             currentWebsiteCertificateRadioButton.setChecked(true);
1743
1744             // Uncheck the saved SSL certificate radio button.
1745             savedSslCertificateRadioButton.setChecked(false);
1746
1747             // Set the background of the current website SSL certificate linear layout to be transparent.
1748             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1749
1750             // Darken the background of the saved SSL certificate linear layout according to the theme.
1751             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1752                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1753             } else {
1754                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1755             }
1756         });
1757
1758         // Set the pinned IP addresses switch listener.
1759         pinnedIpAddressesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1760             // Update the icon.
1761             if (isChecked) {  // IP addresses pinning is enabled.
1762                 // Set the icon.
1763                 pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled, null));
1764
1765                 // Update the visibility of the saved IP addresses card view.
1766                 if (savedIpAddresses == null) {  // There are no saved IP addresses.
1767                     savedIpAddressesCardView.setVisibility(View.GONE);
1768                 } else {  // There are saved IP addresses.
1769                     savedIpAddressesCardView.setVisibility(View.VISIBLE);
1770                 }
1771
1772                 // Show the current IP addresses card view.
1773                 currentIpAddressesCardView.setVisibility(View.VISIBLE);
1774
1775                 // Set the status of the radio buttons.
1776                 if (savedIpAddressesCardView.getVisibility() == View.VISIBLE) {  // The saved IP addresses are visible.
1777                     // Check the saved IP addresses radio button.
1778                     savedIpAddressesRadioButton.setChecked(true);
1779
1780                     // Uncheck the current IP addresses radio button.
1781                     currentIpAddressesRadioButton.setChecked(false);
1782
1783                     // Set the background of the saved IP addresses linear layout to be transparent.
1784                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1785
1786                     // Darken the background of the current IP addresses linear layout according to the theme.
1787                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1788                         currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1789                     } else {
1790                         currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1791                     }
1792                 } else {  // The saved IP addresses are not visible.
1793                     // Check the current IP addresses radio button.
1794                     currentIpAddressesRadioButton.setChecked(true);
1795
1796                     // Uncheck the saved IP addresses radio button.
1797                     savedIpAddressesRadioButton.setChecked(false);
1798
1799                     // Set the background of the current IP addresses linear layout to be transparent.
1800                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1801
1802                     // Darken the background of the saved IP addresses linear layout according to the theme.
1803                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1804                         savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1805                     } else {
1806                         savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1807                     }
1808                 }
1809
1810                 // Scroll to the bottom of the card views.
1811                 currentIpAddressesCardView.getParent().requestChildFocus(currentIpAddressesCardView, currentIpAddressesCardView);
1812             } else {  // IP addresses pinning is disabled.
1813                 // Set the icon.
1814                 pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled, null));
1815
1816                 // Hide the IP addresses card views.
1817                 savedIpAddressesCardView.setVisibility(View.GONE);
1818                 currentIpAddressesCardView.setVisibility(View.GONE);
1819
1820                 // Uncheck the radio buttons.
1821                 savedIpAddressesRadioButton.setChecked(false);
1822                 currentIpAddressesRadioButton.setChecked(false);
1823             }
1824         });
1825
1826         savedIpAddressesCardView.setOnClickListener((View view) -> {
1827             // Check the saved IP addresses radio button.
1828             savedIpAddressesRadioButton.setChecked(true);
1829
1830             // Uncheck the current website IP addresses radio button.
1831             currentIpAddressesRadioButton.setChecked(false);
1832
1833             // Set the background of the saved IP addresses linear layout to be transparent.
1834             savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1835
1836             // Darken the background of the current IP addresses linear layout according to the theme.
1837             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1838                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1839             } else {
1840                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1841             }
1842         });
1843
1844         savedIpAddressesRadioButton.setOnClickListener((View view) -> {
1845             // Check the saved IP addresses radio button.
1846             savedIpAddressesRadioButton.setChecked(true);
1847
1848             // Uncheck the current website IP addresses radio button.
1849             currentIpAddressesRadioButton.setChecked(false);
1850
1851             // Set the background of the saved IP addresses linear layout to be transparent.
1852             savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1853
1854             // Darken the background of the current IP addresses linear layout according to the theme.
1855             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1856                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1857             } else {
1858                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1859             }
1860         });
1861
1862         currentIpAddressesCardView.setOnClickListener((View view) -> {
1863             // Check the current IP addresses radio button.
1864             currentIpAddressesRadioButton.setChecked(true);
1865
1866             // Uncheck the saved IP addresses radio button.
1867             savedIpAddressesRadioButton.setChecked(false);
1868
1869             // Set the background of the current IP addresses linear layout to be transparent.
1870             currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1871
1872             // Darken the background of the saved IP addresses linear layout according to the theme.
1873             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1874                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1875             } else {
1876                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1877             }
1878         });
1879
1880         currentIpAddressesRadioButton.setOnClickListener((View view) -> {
1881             // Check the current IP addresses radio button.
1882             currentIpAddressesRadioButton.setChecked(true);
1883
1884             // Uncheck the saved IP addresses radio button.
1885             savedIpAddressesRadioButton.setChecked(false);
1886
1887             // Set the background of the current IP addresses linear layout to be transparent.
1888             currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1889
1890             // Darken the background of the saved IP addresses linear layout according to the theme.
1891             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1892                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1893             } else {
1894                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1895             }
1896         });
1897
1898         // Set the scroll Y.
1899         domainSettingsScrollView.post(() -> domainSettingsScrollView.setScrollY(scrollY));
1900
1901         // Return the domain settings view.
1902         return domainSettingsView;
1903     }
1904
1905     private boolean checkDomainNameAgainstCertificate(String domainName, String certificateCommonName) {
1906         // Initialize `domainNamesMatch`.
1907         boolean domainNamesMatch = false;
1908
1909         // Check various wildcard permutations if `domainName` and `certificateCommonName` are not empty.
1910         // `noinspection ConstantCondition` removes Android Studio's incorrect lint warning that `domainName` can never be `null`.
1911         if ((domainName != null) && (certificateCommonName != null)) {
1912             // Check if the domains match.
1913             if (domainName.equals(certificateCommonName)) {
1914                 domainNamesMatch = true;
1915             }
1916
1917             // If `domainName` starts with a wildcard, check the base domain against all the subdomains of `certificateCommonName`.
1918             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2)) {
1919                 // Remove the initial `*.`.
1920                 String baseDomainName = domainName.substring(2);
1921
1922                 // Setup a copy of `certificateCommonName` to test subdomains.
1923                 String certificateCommonNameSubdomain = certificateCommonName;
1924
1925                 // Check all the subdomains in `certificateCommonNameSubdomain` against `baseDomainName`.
1926                 while (!domainNamesMatch && certificateCommonNameSubdomain.contains(".")) {  // Stop checking if we know that `domainNamesMatch` is `true` or if we run out of  `.`.
1927                     // Test the `certificateCommonNameSubdomain` against `baseDomainName`.
1928                     if (certificateCommonNameSubdomain.equals(baseDomainName)) {
1929                         domainNamesMatch = true;
1930                     }
1931
1932                     // Strip out the lowest subdomain of `certificateCommonNameSubdomain`.
1933                     try {
1934                         certificateCommonNameSubdomain = certificateCommonNameSubdomain.substring(certificateCommonNameSubdomain.indexOf(".") + 1);
1935                     } catch (IndexOutOfBoundsException e) {  // `certificateCommonNameSubdomain` ends with `.`.
1936                         certificateCommonNameSubdomain = "";
1937                     }
1938                 }
1939             }
1940
1941             // If `certificateCommonName` starts with a wildcard, check the base common name against all the subdomains of `domainName`.
1942             if (!domainNamesMatch && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
1943                 // Remove the initial `*.`.
1944                 String baseCertificateCommonName = certificateCommonName.substring(2);
1945
1946                 // Setup a copy of `domainName` to test subdomains.
1947                 String domainNameSubdomain = domainName;
1948
1949                 // Check all the subdomains in `domainNameSubdomain` against `baseCertificateCommonName`.
1950                 while (!domainNamesMatch && domainNameSubdomain.contains(".") && (domainNameSubdomain.length() > 2)) {
1951                     // Test the `domainNameSubdomain` against `baseCertificateCommonName`.
1952                     if (domainNameSubdomain.equals(baseCertificateCommonName)) {
1953                         domainNamesMatch = true;
1954                     }
1955
1956                     // Strip out the lowest subdomain of `domainNameSubdomain`.
1957                     try {
1958                         domainNameSubdomain = domainNameSubdomain.substring(domainNameSubdomain.indexOf(".") + 1);
1959                     } catch (IndexOutOfBoundsException e) { // `domainNameSubdomain` ends with `.`.
1960                         domainNameSubdomain = "";
1961                     }
1962                 }
1963             }
1964
1965             // If both names start with a wildcard, check if the root of one contains the root of the other.
1966             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2) && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
1967                 // Remove the wildcards.
1968                 String rootDomainName = domainName.substring(2);
1969                 String rootCertificateCommonName = certificateCommonName.substring(2);
1970
1971                 // Check if one name ends with the contents of the other.  If so, there will be overlap in the their wildcard subdomains.
1972                 if (rootDomainName.endsWith(rootCertificateCommonName) || rootCertificateCommonName.endsWith(rootDomainName)) {
1973                     domainNamesMatch = true;
1974                 }
1975             }
1976         }
1977
1978         return domainNamesMatch;
1979     }
1980 }