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