]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java
3a216eb164d588105f9560bbe94036c00c54da23
[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.SharedPreferences;
24 import android.content.res.Configuration;
25 import android.content.res.Resources;
26 import android.database.Cursor;
27 import android.os.Build;
28 import android.os.Bundle;
29 import android.preference.PreferenceManager;
30 import android.text.Editable;
31 import android.text.SpannableStringBuilder;
32 import android.text.Spanned;
33 import android.text.TextWatcher;
34 import android.text.style.ForegroundColorSpan;
35 import android.view.LayoutInflater;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.webkit.WebView;
39 import android.widget.AdapterView;
40 import android.widget.ArrayAdapter;
41 import android.widget.CompoundButton;
42 import android.widget.EditText;
43 import android.widget.ImageView;
44 import android.widget.LinearLayout;
45 import android.widget.RadioButton;
46 import android.widget.ScrollView;
47 import android.widget.Spinner;
48 import android.widget.TextView;
49
50 import androidx.annotation.NonNull;
51 import androidx.appcompat.widget.SwitchCompat;
52 import androidx.cardview.widget.CardView;
53 import androidx.core.content.res.ResourcesCompat;
54 import androidx.fragment.app.Fragment;
55
56 import com.stoutner.privacybrowser.R;
57 import com.stoutner.privacybrowser.activities.DomainsActivity;
58 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
59 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
60
61 import java.text.DateFormat;
62 import java.util.Calendar;
63 import java.util.Date;
64
65 public class DomainSettingsFragment extends Fragment {
66     // Initialize the public class constants.
67     public static final String DATABASE_ID = "database_id";
68     public static final String SCROLL_Y = "scroll_y";
69
70     // Declare the public variables.  `databaseId` is public static so it can be accessed from `DomainsActivity`.
71     public static int databaseId;
72
73     // Declare the class variables.
74     private int scrollY;
75
76     @Override
77     public void onCreate(Bundle savedInstanceState) {
78         // Run the default commands.
79         super.onCreate(savedInstanceState);
80
81         // Remove the lint warning that `getArguments` might be null.
82         assert getArguments() != null;
83
84         // Store the database id in `databaseId`.
85         databaseId = getArguments().getInt(DATABASE_ID);
86         scrollY = getArguments().getInt(SCROLL_Y);
87     }
88
89     @Override
90     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
91         // Inflate `domain_settings_fragment`.  `false` does not attach it to the root `container`.
92         View domainSettingsView = inflater.inflate(R.layout.domain_settings_fragment, container, false);
93
94         // Get handles for the context and the resources.
95         Resources resources = getResources();
96
97         // Get the current theme status.
98         int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
99
100         // Get a handle for the shared preference.
101         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext());
102
103         // Store the default settings.
104         String defaultUserAgentName = sharedPreferences.getString("user_agent", getString(R.string.user_agent_default_value));
105         String defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value));
106         boolean defaultXRequestedWithHeader = sharedPreferences.getBoolean(getString(R.string.x_requested_with_header_key), true);
107         String defaultFontSizeString = sharedPreferences.getString("font_size", getString(R.string.font_size_default_value));
108         boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
109         String defaultWebViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value));
110         boolean defaultWideViewport = sharedPreferences.getBoolean("wide_viewport", true);
111         boolean defaultDisplayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true);
112
113         // Get handles for the views.
114         ScrollView domainSettingsScrollView = domainSettingsView.findViewById(R.id.domain_settings_scrollview);
115         EditText domainNameEditText = domainSettingsView.findViewById(R.id.domain_settings_name_edittext);
116         ImageView javaScriptImageView = domainSettingsView.findViewById(R.id.javascript_imageview);
117         SwitchCompat javaScriptSwitch = domainSettingsView.findViewById(R.id.javascript_switch);
118         ImageView cookiesImageView = domainSettingsView.findViewById(R.id.cookies_imageview);
119         SwitchCompat cookiesSwitch = domainSettingsView.findViewById(R.id.cookies_switch);
120         ImageView domStorageImageView = domainSettingsView.findViewById(R.id.dom_storage_imageview);
121         SwitchCompat domStorageSwitch = domainSettingsView.findViewById(R.id.dom_storage_switch);
122         ImageView formDataImageView = domainSettingsView.findViewById(R.id.form_data_imageview);  // The form data views can be remove once the minimum API >= 26.
123         SwitchCompat formDataSwitch = domainSettingsView.findViewById(R.id.form_data_switch);  // The form data views can be remove once the minimum API >= 26.
124         ImageView easyListImageView = domainSettingsView.findViewById(R.id.easylist_imageview);
125         SwitchCompat easyListSwitch = domainSettingsView.findViewById(R.id.easylist_switch);
126         ImageView easyPrivacyImageView = domainSettingsView.findViewById(R.id.easyprivacy_imageview);
127         SwitchCompat easyPrivacySwitch = domainSettingsView.findViewById(R.id.easyprivacy_switch);
128         ImageView fanboysAnnoyanceListImageView = domainSettingsView.findViewById(R.id.fanboys_annoyance_list_imageview);
129         SwitchCompat fanboysAnnoyanceListSwitch = domainSettingsView.findViewById(R.id.fanboys_annoyance_list_switch);
130         ImageView fanboysSocialBlockingListImageView = domainSettingsView.findViewById(R.id.fanboys_social_blocking_list_imageview);
131         SwitchCompat fanboysSocialBlockingListSwitch = domainSettingsView.findViewById(R.id.fanboys_social_blocking_list_switch);
132         ImageView ultraListImageView = domainSettingsView.findViewById(R.id.ultralist_imageview);
133         SwitchCompat ultraListSwitch = domainSettingsView.findViewById(R.id.ultralist_switch);
134         ImageView ultraPrivacyImageView = domainSettingsView.findViewById(R.id.ultraprivacy_imageview);
135         SwitchCompat ultraPrivacySwitch = domainSettingsView.findViewById(R.id.ultraprivacy_switch);
136         ImageView blockAllThirdPartyRequestsImageView = domainSettingsView.findViewById(R.id.block_all_third_party_requests_imageview);
137         SwitchCompat blockAllThirdPartyRequestsSwitch = domainSettingsView.findViewById(R.id.block_all_third_party_requests_switch);
138         Spinner userAgentSpinner = domainSettingsView.findViewById(R.id.user_agent_spinner);
139         TextView userAgentTextView = domainSettingsView.findViewById(R.id.user_agent_textview);
140         EditText customUserAgentEditText = domainSettingsView.findViewById(R.id.custom_user_agent_edittext);
141         ImageView xRequestedWithHeaderImageView = domainSettingsView.findViewById(R.id.x_requested_with_header_imageview);
142         Spinner xRequestedWithHeaderSpinner = domainSettingsView.findViewById(R.id.x_requested_with_header_spinner);
143         TextView xRequestedWithHeaderTextView = domainSettingsView.findViewById(R.id.x_requested_with_header_textview);
144         TextView xRequestedWithHeaderExplanationTextView = domainSettingsView.findViewById(R.id.x_requested_with_header_explanation_textview);
145         Spinner fontSizeSpinner = domainSettingsView.findViewById(R.id.font_size_spinner);
146         TextView defaultFontSizeTextView = domainSettingsView.findViewById(R.id.default_font_size_textview);
147         EditText customFontSizeEditText = domainSettingsView.findViewById(R.id.custom_font_size_edittext);
148         ImageView swipeToRefreshImageView = domainSettingsView.findViewById(R.id.swipe_to_refresh_imageview);
149         Spinner swipeToRefreshSpinner = domainSettingsView.findViewById(R.id.swipe_to_refresh_spinner);
150         TextView swipeToRefreshTextView = domainSettingsView.findViewById(R.id.swipe_to_refresh_textview);
151         ImageView webViewThemeImageView = domainSettingsView.findViewById(R.id.webview_theme_imageview);
152         Spinner webViewThemeSpinner = domainSettingsView.findViewById(R.id.webview_theme_spinner);
153         TextView webViewThemeTextView = domainSettingsView.findViewById(R.id.webview_theme_textview);
154         ImageView wideViewportImageView = domainSettingsView.findViewById(R.id.wide_viewport_imageview);
155         Spinner wideViewportSpinner = domainSettingsView.findViewById(R.id.wide_viewport_spinner);
156         TextView wideViewportTextView = domainSettingsView.findViewById(R.id.wide_viewport_textview);
157         ImageView displayWebpageImagesImageView = domainSettingsView.findViewById(R.id.display_webpage_images_imageview);
158         Spinner displayWebpageImagesSpinner = domainSettingsView.findViewById(R.id.display_webpage_images_spinner);
159         TextView displayImagesTextView = domainSettingsView.findViewById(R.id.display_webpage_images_textview);
160         ImageView pinnedSslCertificateImageView = domainSettingsView.findViewById(R.id.pinned_ssl_certificate_imageview);
161         SwitchCompat pinnedSslCertificateSwitch = domainSettingsView.findViewById(R.id.pinned_ssl_certificate_switch);
162         CardView savedSslCardView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_cardview);
163         LinearLayout savedSslCertificateLinearLayout = domainSettingsView.findViewById(R.id.saved_ssl_certificate_linearlayout);
164         RadioButton savedSslCertificateRadioButton = domainSettingsView.findViewById(R.id.saved_ssl_certificate_radiobutton);
165         TextView savedSslIssuedToCNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_cname);
166         TextView savedSslIssuedToONameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_oname);
167         TextView savedSslIssuedToUNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_uname);
168         TextView savedSslIssuedByCNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_cname);
169         TextView savedSslIssuedByONameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_oname);
170         TextView savedSslIssuedByUNameTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_uname);
171         TextView savedSslStartDateTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_start_date);
172         TextView savedSslEndDateTextView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_end_date);
173         CardView currentSslCardView = domainSettingsView.findViewById(R.id.current_website_certificate_cardview);
174         LinearLayout currentWebsiteCertificateLinearLayout = domainSettingsView.findViewById(R.id.current_website_certificate_linearlayout);
175         RadioButton currentWebsiteCertificateRadioButton = domainSettingsView.findViewById(R.id.current_website_certificate_radiobutton);
176         TextView currentSslIssuedToCNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_cname);
177         TextView currentSslIssuedToONameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_oname);
178         TextView currentSslIssuedToUNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_uname);
179         TextView currentSslIssuedByCNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_cname);
180         TextView currentSslIssuedByONameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_oname);
181         TextView currentSslIssuedByUNameTextView = domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_uname);
182         TextView currentSslStartDateTextView = domainSettingsView.findViewById(R.id.current_website_certificate_start_date);
183         TextView currentSslEndDateTextView = domainSettingsView.findViewById(R.id.current_website_certificate_end_date);
184         TextView noCurrentWebsiteCertificateTextView = domainSettingsView.findViewById(R.id.no_current_website_certificate);
185         ImageView pinnedIpAddressesImageView = domainSettingsView.findViewById(R.id.pinned_ip_addresses_imageview);
186         SwitchCompat pinnedIpAddressesSwitch = domainSettingsView.findViewById(R.id.pinned_ip_addresses_switch);
187         CardView savedIpAddressesCardView = domainSettingsView.findViewById(R.id.saved_ip_addresses_cardview);
188         LinearLayout savedIpAddressesLinearLayout = domainSettingsView.findViewById(R.id.saved_ip_addresses_linearlayout);
189         RadioButton savedIpAddressesRadioButton = domainSettingsView.findViewById(R.id.saved_ip_addresses_radiobutton);
190         TextView savedIpAddressesTextView = domainSettingsView.findViewById(R.id.saved_ip_addresses_textview);
191         CardView currentIpAddressesCardView = domainSettingsView.findViewById(R.id.current_ip_addresses_cardview);
192         LinearLayout currentIpAddressesLinearLayout = domainSettingsView.findViewById(R.id.current_ip_addresses_linearlayout);
193         RadioButton currentIpAddressesRadioButton = domainSettingsView.findViewById(R.id.current_ip_addresses_radiobutton);
194         TextView currentIpAddressesTextView = domainSettingsView.findViewById(R.id.current_ip_addresses_textview);
195
196         // Setup the pinned labels.
197         String cNameLabel = getString(R.string.common_name) + "  ";
198         String oNameLabel = getString(R.string.organization) + "  ";
199         String uNameLabel = getString(R.string.organizational_unit) + "  ";
200         String startDateLabel = getString(R.string.start_date) + "  ";
201         String endDateLabel = getString(R.string.end_date) + "  ";
202
203         // Initialize the database handler.
204         DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(requireContext());
205
206         // Get the database cursor for this ID and move it to the first row.
207         Cursor domainCursor = domainsDatabaseHelper.getCursorForId(databaseId);
208         domainCursor.moveToFirst();
209
210         // Save the cursor entries as variables.
211         String domainNameString = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME));
212         int javaScriptInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT));
213         int cookiesInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES));
214         int domStorageInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE));
215         int formDataInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA));  // Form data can be remove once the minimum API >= 26.
216         int easyListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST));
217         int easyPrivacyInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY));
218         int fanboysAnnoyanceListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST));
219         int fanboysSocialBlockingListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST));
220         int ultraListInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST));
221         int ultraPrivacyInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY));
222         int blockAllThirdPartyRequestsInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS));
223         String currentUserAgentName = domainCursor.getString(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT));
224         int xRequestedWithHeaderInt = domainCursor.getInt(domainCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.X_REQUESTED_WITH_HEADER));
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(requireContext(), R.array.translated_domain_settings_user_agent_names, R.layout.spinner_item);
255         ArrayAdapter<CharSequence> xRequestedWithHeaderArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.x_requested_with_header_array, R.layout.spinner_item);
256         ArrayAdapter<CharSequence> fontSizeArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.font_size_array, R.layout.spinner_item);
257         ArrayAdapter<CharSequence> swipeToRefreshArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.swipe_to_refresh_array, R.layout.spinner_item);
258         ArrayAdapter<CharSequence> webViewThemeArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.webview_theme_array, R.layout.spinner_item);
259         ArrayAdapter<CharSequence> wideViewportArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.wide_viewport_array, R.layout.spinner_item);
260         ArrayAdapter<CharSequence> displayImagesArrayAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.display_webpage_images_array, R.layout.spinner_item);
261
262         // Set the drop down view resource on the spinners.
263         translatedUserAgentArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
264         xRequestedWithHeaderArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
265         fontSizeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
266         swipeToRefreshArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
267         webViewThemeArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
268         wideViewportArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
269         displayImagesArrayAdapter.setDropDownViewResource(R.layout.domain_settings_spinner_dropdown_items);
270
271         // Set the array adapters for the spinners.
272         userAgentSpinner.setAdapter(translatedUserAgentArrayAdapter);
273         xRequestedWithHeaderSpinner.setAdapter(xRequestedWithHeaderArrayAdapter);
274         fontSizeSpinner.setAdapter(fontSizeArrayAdapter);
275         swipeToRefreshSpinner.setAdapter(swipeToRefreshArrayAdapter);
276         webViewThemeSpinner.setAdapter(webViewThemeArrayAdapter);
277         wideViewportSpinner.setAdapter(wideViewportArrayAdapter);
278         displayWebpageImagesSpinner.setAdapter(displayImagesArrayAdapter);
279
280         // Create a spannable string builder for each TextView that needs multiple colors of text.
281         SpannableStringBuilder savedSslIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString);
282         SpannableStringBuilder savedSslIssuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + savedSslIssuedToONameString);
283         SpannableStringBuilder savedSslIssuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + savedSslIssuedToUNameString);
284         SpannableStringBuilder savedSslIssuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslIssuedByCNameString);
285         SpannableStringBuilder savedSslIssuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + savedSslIssuedByONameString);
286         SpannableStringBuilder savedSslIssuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + savedSslIssuedByUNameString);
287
288         // Initialize the spannable string builders for the saved SSL certificate dates.
289         SpannableStringBuilder savedSslStartDateStringBuilder;
290         SpannableStringBuilder savedSslEndDateStringBuilder;
291
292         // Leave the SSL certificate dates empty if they are `null`.
293         if (savedSslStartDate == null) {
294             savedSslStartDateStringBuilder = new SpannableStringBuilder(startDateLabel);
295         } else {
296             savedSslStartDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslStartDate));
297         }
298
299         if (savedSslEndDate == null) {
300             savedSslEndDateStringBuilder = new SpannableStringBuilder(endDateLabel);
301         } else {
302             savedSslEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslEndDate));
303         }
304
305         // Create the color spans.
306         final ForegroundColorSpan blueColorSpan = new ForegroundColorSpan(requireContext().getColor(R.color.alt_blue_text));
307         final ForegroundColorSpan redColorSpan = new ForegroundColorSpan(requireContext().getColor(R.color.red_text));
308
309         // Set the domain name from the the database cursor.
310         domainNameEditText.setText(domainNameString);
311
312         // Update the certificates' `Common Name` color when the domain name text changes.
313         domainNameEditText.addTextChangedListener(new TextWatcher() {
314             @Override
315             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
316                 // Do nothing.
317             }
318
319             @Override
320             public void onTextChanged(CharSequence s, int start, int before, int count) {
321                 // Do nothing.
322             }
323
324             @Override
325             public void afterTextChanged(Editable s) {
326                 // Get the new domain name.
327                 String newDomainName = domainNameEditText.getText().toString();
328
329                 // Check the saved SSL certificate against the new domain name.
330                 boolean savedSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, savedSslIssuedToCNameString);
331
332                 // Create a `SpannableStringBuilder` for the saved certificate `Common Name`.
333                 SpannableStringBuilder savedSslCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslIssuedToCNameString);
334
335                 // Format the saved certificate `Common Name` color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
336                 if (savedSslMatchesNewDomainName) {
337                     savedSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
338                 } else {
339                     savedSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), savedSslCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
340                 }
341
342                 // Update the saved SSL issued to CName text view.
343                 savedSslIssuedToCNameTextView.setText(savedSslCNameStringBuilder);
344
345                 // Update the current website certificate if it exists.
346                 if (DomainsActivity.sslIssuedToCName != null) {
347                     // Check the current website certificate against the new domain name.
348                     boolean currentSslMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, DomainsActivity.sslIssuedToCName);
349
350                     // Create a `SpannableStringBuilder` for the current website certificate `Common Name`.
351                     SpannableStringBuilder currentSslCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName);
352
353                     // Format the current certificate `Common Name` color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
354                     if (currentSslMatchesNewDomainName) {
355                         currentSslCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
356                     } else {
357                         currentSslCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), currentSslCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
358                     }
359
360                     // Update the current SSL issued to CName text view.
361                     currentSslIssuedToCNameTextView.setText(currentSslCNameStringBuilder);
362                 }
363             }
364         });
365
366         // Set the switch positions.
367         javaScriptSwitch.setChecked(javaScriptInt == 1);
368         cookiesSwitch.setChecked(cookiesInt == 1);
369         domStorageSwitch.setChecked(domStorageInt == 1);
370         formDataSwitch.setChecked(formDataInt == 1);  // Form data can be removed once the minimum API >= 26.
371         easyListSwitch.setChecked(easyListInt == 1);
372         easyPrivacySwitch.setChecked(easyPrivacyInt == 1);
373         fanboysAnnoyanceListSwitch.setChecked(fanboysAnnoyanceListInt == 1);
374         fanboysSocialBlockingListSwitch.setChecked(fanboysSocialBlockingListInt == 1);
375         ultraListSwitch.setChecked(ultraListInt == 1);
376         ultraPrivacySwitch.setChecked(ultraPrivacyInt == 1);
377         blockAllThirdPartyRequestsSwitch.setChecked(blockAllThirdPartyRequestsInt == 1);
378         pinnedSslCertificateSwitch.setChecked(pinnedSslCertificateInt == 1);
379         pinnedIpAddressesSwitch.setChecked(pinnedIpAddressesInt == 1);
380
381         // Set the switch icon colors.
382         cookiesImageView.setSelected(cookiesInt == 1);
383         domStorageImageView.setSelected(domStorageInt == 1);
384         formDataImageView.setSelected(formDataInt == 1);  // Form data can be removed once the minimum API >= 26.
385         easyListImageView.setSelected(easyListInt == 1);
386         easyPrivacyImageView.setSelected(easyPrivacyInt == 1);
387         fanboysAnnoyanceListImageView.setSelected(fanboysAnnoyanceListInt == 1);
388         fanboysSocialBlockingListImageView.setSelected(fanboysSocialBlockingListInt == 1);
389         ultraListImageView.setSelected(ultraListInt == 1);
390         ultraPrivacyImageView.setSelected(ultraPrivacyInt == 1);
391         blockAllThirdPartyRequestsImageView.setSelected(blockAllThirdPartyRequestsInt == 1);
392         pinnedSslCertificateImageView.setSelected(pinnedSslCertificateInt == 1);
393         pinnedIpAddressesImageView.setSelected(pinnedIpAddressesInt == 1);
394
395         // Set the JavaScript icon.
396         if (javaScriptInt == 1)
397             javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null));
398         else
399             javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null));
400
401         // Set the DOM storage switch status based on the JavaScript status.
402         domStorageSwitch.setEnabled(javaScriptInt == 1);
403
404         // Set the DOM storage icon ghosted status based on the JavaScript status.
405         domStorageImageView.setEnabled(javaScriptInt == 1);
406
407         // Set the form data visibility.  Form data can be removed once the minimum API >= 26.
408         if (Build.VERSION.SDK_INT >= 26) {
409             // Hide the form data image view and switch.
410             formDataImageView.setVisibility(View.GONE);
411             formDataSwitch.setVisibility(View.GONE);
412         }
413
414         // Set Fanboy's Social Blocking List switch status based on the Annoyance List status.
415         fanboysSocialBlockingListSwitch.setEnabled(fanboysAnnoyanceListInt == 0);
416
417         // Set the Social Blocking List icon ghosted status based on the Annoyance List status.
418         fanboysSocialBlockingListImageView.setEnabled(fanboysAnnoyanceListInt == 0);
419
420         // Inflated a WebView to get the default user agent.
421         // `@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.
422         @SuppressLint("InflateParams") View bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false);
423         WebView bareWebView = bareWebViewLayout.findViewById(R.id.bare_webview);
424         final String webViewDefaultUserAgentString = bareWebView.getSettings().getUserAgentString();
425
426         // Get a handle for the user agent array adapter.  This array does not contain the `System default` entry.
427         ArrayAdapter<CharSequence> userAgentNamesArray = ArrayAdapter.createFromResource(requireContext(), R.array.user_agent_names, R.layout.spinner_item);
428
429         // Get the positions of the user agent and the default user agent.
430         int userAgentArrayPosition = userAgentNamesArray.getPosition(currentUserAgentName);
431         int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
432
433         // Get a handle for the user agent data array.  This array does not contain the `System default` entry.
434         String[] userAgentDataArray = resources.getStringArray(R.array.user_agent_data);
435
436         // Set the user agent text.
437         if (currentUserAgentName.equals(getString(R.string.system_default_user_agent))) {  // Use the system default user agent.
438             // Set the user agent according to the system default.
439             switch (defaultUserAgentArrayPosition) {
440                 case MainWebViewActivity.UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
441                     // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
442                     userAgentTextView.setText(defaultUserAgentName);
443                     break;
444
445                 case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
446                     // Display the `WebView` default user agent.
447                     userAgentTextView.setText(webViewDefaultUserAgentString);
448                     break;
449
450                 case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
451                     // Display the custom user agent.
452                     userAgentTextView.setText(defaultCustomUserAgentString);
453                     break;
454
455                 default:
456                     // Get the user agent string from the user agent data array.
457                     userAgentTextView.setText(userAgentDataArray[defaultUserAgentArrayPosition]);
458             }
459         } else if (userAgentArrayPosition == MainWebViewActivity.UNRECOGNIZED_USER_AGENT || currentUserAgentName.equals(getString(R.string.custom_user_agent))) {
460             // A custom user agent is stored in the current user agent name.  The second check is necessary in case the user did not change the default custom text.
461             // Set the user agent spinner to `Custom user agent`.
462             userAgentSpinner.setSelection(MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT);
463
464             // Hide the user agent TextView.
465             userAgentTextView.setVisibility(View.GONE);
466
467             // Show the custom user agent EditText and set the current user agent name as the text.
468             customUserAgentEditText.setVisibility(View.VISIBLE);
469             customUserAgentEditText.setText(currentUserAgentName);
470         } else {  // The user agent name contains one of the canonical user agents.
471             // 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.
472             userAgentSpinner.setSelection(userAgentArrayPosition + 1);
473
474             // Show the user agent TextView.
475             userAgentTextView.setVisibility(View.VISIBLE);
476
477             // Hide the custom user agent EditText.
478             customUserAgentEditText.setVisibility(View.GONE);
479
480             // Set the user agent text.
481             if (userAgentArrayPosition == MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT) {  // The WebView default user agent is selected.
482                 // Display the WebView default user agent.
483                 userAgentTextView.setText(webViewDefaultUserAgentString);
484             } else {  // A user agent besides the default is selected.
485                 // 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.
486                 userAgentTextView.setText(userAgentDataArray[userAgentArrayPosition + 1]);
487             }
488         }
489
490         // Open the user agent spinner when the text view is clicked.
491         userAgentTextView.setOnClickListener((View v) -> {
492             // Open the user agent spinner.
493             userAgentSpinner.performClick();
494         });
495
496         // Select the X-Requested-With header selection in the spinner.
497         xRequestedWithHeaderSpinner.setSelection(xRequestedWithHeaderInt);
498
499         // Set the X-Requested-With header text.
500         if (defaultXRequestedWithHeader)
501             xRequestedWithHeaderTextView.setText(xRequestedWithHeaderArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
502         else
503             xRequestedWithHeaderTextView.setText(xRequestedWithHeaderArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
504
505         // Set the X-Requested-With header icon and text view settings.
506         switch (xRequestedWithHeaderInt) {
507             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
508                 // Set the icon color.
509                 xRequestedWithHeaderImageView.setSelected(defaultXRequestedWithHeader);
510
511                 // Show the X-Requested-With header text view.
512                 xRequestedWithHeaderTextView.setVisibility(View.VISIBLE);
513                 break;
514
515             case DomainsDatabaseHelper.ENABLED:
516                 // Set the icon color.
517                 xRequestedWithHeaderImageView.setSelected(true);
518
519                 // Hide the X-Requested-With header text view.
520                 xRequestedWithHeaderTextView.setVisibility(View.GONE);
521                 break;
522
523             case DomainsDatabaseHelper.DISABLED:
524                 // Set the icon color.
525                 xRequestedWithHeaderImageView.setSelected(false);
526
527                 // Hide the X-Requested-With header text view.
528                 xRequestedWithHeaderTextView.setVisibility(View.GONE);
529                 break;
530         }
531
532         // Open the X-Requested-With header spinner when the text view is clicked.
533         xRequestedWithHeaderTextView.setOnClickListener((View v) -> {
534             // Open the X-Requested-With header spinner.
535             xRequestedWithHeaderSpinner.performClick();
536         });
537
538         // Open the X-Requested-With header spinner when the explanation text view is clicked.
539         xRequestedWithHeaderExplanationTextView.setOnClickListener((View v) -> {
540             // Open the X-Requested header spinner.
541             xRequestedWithHeaderSpinner.performClick();
542         });
543
544         // Display the font size settings.
545         if (fontSizeInt == 0) {  // `0` is the code for system default font size.
546             // Set the font size to the system default
547             fontSizeSpinner.setSelection(0);
548
549             // Show the default font size text view.
550             defaultFontSizeTextView.setVisibility(View.VISIBLE);
551
552             // Hide the custom font size edit text.
553             customFontSizeEditText.setVisibility(View.GONE);
554
555             // 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.
556             customFontSizeEditText.setText(defaultFontSizeString);
557         } else {  // A custom font size is selected.
558             // Set the spinner to the custom font size.
559             fontSizeSpinner.setSelection(1);
560
561             // Hide the default font size text view.
562             defaultFontSizeTextView.setVisibility(View.GONE);
563
564             // Show the custom font size edit text.
565             customFontSizeEditText.setVisibility(View.GONE);
566
567             // Set the custom font size.
568             customFontSizeEditText.setText(String.valueOf(fontSizeInt));
569         }
570
571         // Initialize the default font size percentage string.
572         String defaultFontSizePercentageString = defaultFontSizeString + "%";
573
574         // Set the default font size text in the text view.
575         defaultFontSizeTextView.setText(defaultFontSizePercentageString);
576
577         // Open the font size spinner when the text view is clicked.
578         defaultFontSizeTextView.setOnClickListener((View v) -> {
579             // Open the user agent spinner.
580             fontSizeSpinner.performClick();
581         });
582
583         // Select the swipe to refresh selection in the spinner.
584         swipeToRefreshSpinner.setSelection(swipeToRefreshInt);
585
586         // Set the swipe to refresh text.
587         if (defaultSwipeToRefresh)
588             swipeToRefreshTextView.setText(swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
589         else
590             swipeToRefreshTextView.setText(swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
591
592         // Set the swipe to refresh icon and text view settings.
593         switch (swipeToRefreshInt) {
594             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
595                 // Set the icon color.
596                 swipeToRefreshImageView.setSelected(defaultSwipeToRefresh);
597
598                 // Show the swipe to refresh text view.
599                 swipeToRefreshTextView.setVisibility(View.VISIBLE);
600                 break;
601
602             case DomainsDatabaseHelper.ENABLED:
603                 // Set the icon color.
604                 swipeToRefreshImageView.setSelected(true);
605
606                 // Hide the swipe to refresh text view.
607                 swipeToRefreshTextView.setVisibility(View.GONE);
608                 break;
609
610             case DomainsDatabaseHelper.DISABLED:
611                 // Set the icon color.
612                 swipeToRefreshImageView.setSelected(false);
613
614                 // Hide the swipe to refresh text view.
615                 swipeToRefreshTextView.setVisibility(View.GONE);
616                 break;
617         }
618
619         // Open the swipe to refresh spinner when the text view is clicked.
620         swipeToRefreshTextView.setOnClickListener((View v) -> {
621             // Open the swipe to refresh spinner.
622             swipeToRefreshSpinner.performClick();
623         });
624
625         // Get the WebView theme string arrays.
626         String[] webViewThemeStringArray = resources.getStringArray(R.array.webview_theme_array);
627         String[] webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values);
628
629         // Define an app WebView theme entry number.
630         int appWebViewThemeEntryNumber;
631
632         // 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.
633         if (defaultWebViewTheme.equals(webViewThemeEntryValuesStringArray[1])) {  // The light theme is selected.
634             // Store the default WebView theme entry number.
635             appWebViewThemeEntryNumber = 1;
636         } else if (defaultWebViewTheme.equals(webViewThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
637             // Store the default WebView theme entry number.
638             appWebViewThemeEntryNumber = 2;
639         } else {  // The system default theme is selected.
640             // Store the default WebView theme entry number.
641             appWebViewThemeEntryNumber = 0;
642         }
643
644         // Select the WebView theme in the spinner.
645         webViewThemeSpinner.setSelection(webViewThemeInt);
646
647         // Set the WebView theme text.
648         if (appWebViewThemeEntryNumber == DomainsDatabaseHelper.SYSTEM_DEFAULT) {  // The app WebView theme is system default.
649             // Set the text according to the current UI theme.
650             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
651                 webViewThemeTextView.setText(webViewThemeStringArray[DomainsDatabaseHelper.LIGHT_THEME]);
652             } else {
653                 webViewThemeTextView.setText(webViewThemeStringArray[DomainsDatabaseHelper.DARK_THEME]);
654             }
655         } else {  // The app WebView theme is not system default.
656             // Set the text according to the app WebView theme.
657             webViewThemeTextView.setText(webViewThemeStringArray[appWebViewThemeEntryNumber]);
658         }
659
660         // Set the WebView theme icon and text visibility.
661         switch (webViewThemeInt) {
662             case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // The domain WebView theme is system default.
663                 // Set the icon according to the app WebView theme.
664                 switch (appWebViewThemeEntryNumber) {
665                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // The default WebView theme is system default.
666                         // Set the icon color.
667                         webViewThemeImageView.setSelected(currentThemeStatus == Configuration.UI_MODE_NIGHT_NO);
668                         break;
669
670                     case DomainsDatabaseHelper.LIGHT_THEME:  // the default WebView theme is light.
671                         // Set the icon color.
672                         webViewThemeImageView.setSelected(true);
673                         break;
674
675                     case DomainsDatabaseHelper.DARK_THEME:  // the default WebView theme is dark.
676                         // Set the icon color.
677                         webViewThemeImageView.setSelected(false);
678                         break;
679                 }
680
681                 // Show the WebView theme text view.
682                 webViewThemeTextView.setVisibility(View.VISIBLE);
683                 break;
684
685             case DomainsDatabaseHelper.LIGHT_THEME:  // The domain WebView theme is light.
686                 // Set the icon color.
687                 webViewThemeImageView.setSelected(true);
688
689                 // Hide the WebView theme text view.
690                 webViewThemeTextView.setVisibility(View.GONE);
691                 break;
692
693             case DomainsDatabaseHelper.DARK_THEME:  // The domain WebView theme is dark.
694                 // Set the icon color.
695                 webViewThemeImageView.setSelected(false);
696
697                 // Hide the WebView theme text view.
698                 webViewThemeTextView.setVisibility(View.GONE);
699                 break;
700         }
701
702         // Open the WebView theme spinner when the text view is clicked.
703         webViewThemeTextView.setOnClickListener((View v) -> {
704             // Open the WebView theme spinner.
705             webViewThemeSpinner.performClick();
706         });
707
708         // Select the wide viewport in the spinner.
709         wideViewportSpinner.setSelection(wideViewportInt);
710
711         // Set the default wide viewport text.
712         if (defaultWideViewport)
713             wideViewportTextView.setText(wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
714         else
715             wideViewportTextView.setText(wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
716
717         // Set the wide viewport icon and text view settings.
718         switch (wideViewportInt) {
719             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
720                 // Set the icon color.
721                 wideViewportImageView.setSelected(defaultWideViewport);
722
723                 // Show the wide viewport text view.
724                 wideViewportTextView.setVisibility(View.VISIBLE);
725                 break;
726
727             case DomainsDatabaseHelper.ENABLED:
728                 // Set the icon color.
729                 wideViewportImageView.setSelected(true);
730
731                 // Hide the wide viewport text view.
732                 wideViewportTextView.setVisibility(View.GONE);
733                 break;
734
735             case DomainsDatabaseHelper.DISABLED:
736                 // Set the icon color.
737                 wideViewportImageView.setSelected(false);
738
739                 // Hide the wide viewport text view.
740                 wideViewportTextView.setVisibility(View.GONE);
741                 break;
742         }
743
744         // Open the wide viewport spinner when the text view is clicked.
745         wideViewportTextView.setOnClickListener((View view) -> {
746             // Open the wide viewport spinner.
747             wideViewportSpinner.performClick();
748         });
749
750         // Display the website images mode in the spinner.
751         displayWebpageImagesSpinner.setSelection(displayImagesInt);
752
753         // Set the default display images text.
754         if (defaultDisplayWebpageImages) {
755             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
756         } else {
757             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
758         }
759
760         // Set the display website images icon and text view settings.
761         switch (displayImagesInt) {
762             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
763                 // Set the icon color.
764                 displayWebpageImagesImageView.setSelected(defaultDisplayWebpageImages);
765
766                 // Show the display images text view.
767                 displayImagesTextView.setVisibility(View.VISIBLE);
768                 break;
769
770             case DomainsDatabaseHelper.ENABLED:
771                 // Set the icon color.
772                 displayWebpageImagesImageView.setSelected(true);
773
774                 // Hide the display images text view.
775                 displayImagesTextView.setVisibility(View.GONE);
776                 break;
777
778             case DomainsDatabaseHelper.DISABLED:
779                 // Set the icon color.
780                 displayWebpageImagesImageView.setSelected(false);
781
782                 // Hide the display images text view.
783                 displayImagesTextView.setVisibility(View.GONE);
784                 break;
785         }
786
787         // Open the display images spinner when the text view is clicked.
788         displayImagesTextView.setOnClickListener((View view) -> {
789             // Open the user agent spinner.
790             displayWebpageImagesSpinner.performClick();
791         });
792
793         // Store the current date.
794         Date currentDate = Calendar.getInstance().getTime();
795
796         // Setup the string builders to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
797         savedSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
798         savedSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
799         savedSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
800         savedSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
801         savedSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
802
803         // Check the certificate Common Name against the domain name.
804         boolean savedSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslIssuedToCNameString);
805
806         // Format the issued to Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
807         if (savedSslCommonNameMatchesDomainName) {
808             savedSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
809         } else {
810             savedSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), savedSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
811         }
812
813         //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
814         if ((savedSslStartDate != null) && savedSslStartDate.after(currentDate)) {  // The certificate start date is in the future.
815             savedSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), savedSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
816         } else {  // The certificate start date is in the past.
817             savedSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), savedSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
818         }
819
820         // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
821         if ((savedSslEndDate != null) && savedSslEndDate.before(currentDate)) {  // The certificate end date is in the past.
822             savedSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), savedSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
823         } else {  // The certificate end date is in the future.
824             savedSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), savedSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
825         }
826
827         // Display the saved website SSL certificate strings.
828         savedSslIssuedToCNameTextView.setText(savedSslIssuedToCNameStringBuilder);
829         savedSslIssuedToONameTextView.setText(savedSslIssuedToONameStringBuilder);
830         savedSslIssuedToUNameTextView.setText(savedSslIssuedToUNameStringBuilder);
831         savedSslIssuedByCNameTextView.setText(savedSslIssuedByCNameStringBuilder);
832         savedSslIssuedByONameTextView.setText(savedSslIssuedByONameStringBuilder);
833         savedSslIssuedByUNameTextView.setText(savedSslIssuedByUNameStringBuilder);
834         savedSslStartDateTextView.setText(savedSslStartDateStringBuilder);
835         savedSslEndDateTextView.setText(savedSslEndDateStringBuilder);
836
837         // Populate the current website SSL certificate if there is one.
838         if (DomainsActivity.sslIssuedToCName != null) {
839             // Get dates from the raw long values.
840             Date currentSslStartDate = new Date(DomainsActivity.sslStartDateLong);
841             Date currentSslEndDate = new Date(DomainsActivity.sslEndDateLong);
842
843             // Create a spannable string builder for each text view that needs multiple colors of text.
844             SpannableStringBuilder currentSslIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName);
845             SpannableStringBuilder currentSslIssuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedToOName);
846             SpannableStringBuilder currentSslIssuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedToUName);
847             SpannableStringBuilder currentSslIssuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedByCName);
848             SpannableStringBuilder currentSslIssuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedByOName);
849             SpannableStringBuilder currentSslIssuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedByUName);
850             SpannableStringBuilder currentSslStartDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
851                     .format(currentSslStartDate));
852             SpannableStringBuilder currentSslEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
853                     .format(currentSslEndDate));
854
855             // Setup the string builders to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
856             currentSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentSslIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
857             currentSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentSslIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
858             currentSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
859             currentSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentSslIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
860             currentSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentSslIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
861
862             // Check the certificate Common Name against the domain name.
863             boolean currentSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, DomainsActivity.sslIssuedToCName);
864
865             // Format the issued to Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
866             if (currentSslCommonNameMatchesDomainName) {
867                 currentSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
868             } else {
869                 currentSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), currentSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
870             }
871
872             //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
873             if (currentSslStartDate.after(currentDate)) {  // The certificate start date is in the future.
874                 currentSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), currentSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
875             } else {  // The certificate start date is in the past.
876                 currentSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), currentSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
877             }
878
879             // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
880             if (currentSslEndDate.before(currentDate)) {  // The certificate end date is in the past.
881                 currentSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), currentSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
882             } else {  // The certificate end date is in the future.
883                 currentSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), currentSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
884             }
885
886             // Display the current website SSL certificate strings.
887             currentSslIssuedToCNameTextView.setText(currentSslIssuedToCNameStringBuilder);
888             currentSslIssuedToONameTextView.setText(currentSslIssuedToONameStringBuilder);
889             currentSslIssuedToUNameTextView.setText(currentSslIssuedToUNameStringBuilder);
890             currentSslIssuedByCNameTextView.setText(currentSslIssuedByCNameStringBuilder);
891             currentSslIssuedByONameTextView.setText(currentSslIssuedByONameStringBuilder);
892             currentSslIssuedByUNameTextView.setText(currentSslIssuedByUNameStringBuilder);
893             currentSslStartDateTextView.setText(currentSslStartDateStringBuilder);
894             currentSslEndDateTextView.setText(currentSslEndDateStringBuilder);
895         }
896
897         // Set the initial display status of the SSL certificates card views.
898         if (pinnedSslCertificateSwitch.isChecked()) {  // An SSL certificate is pinned.
899             // Set the visibility of the saved SSL certificate.
900             if (savedSslIssuedToCNameString == null) {
901                 savedSslCardView.setVisibility(View.GONE);
902             } else {
903                 savedSslCardView.setVisibility(View.VISIBLE);
904             }
905
906             // Set the visibility of the current website SSL certificate.
907             if (DomainsActivity.sslIssuedToCName == null) {  // There is no current SSL certificate.
908                 // Hide the SSL certificate.
909                 currentSslCardView.setVisibility(View.GONE);
910
911                 // Show the instruction.
912                 noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
913             } else {  // There is a current SSL certificate.
914                 // Show the SSL certificate.
915                 currentSslCardView.setVisibility(View.VISIBLE);
916
917                 // Hide the instruction.
918                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
919             }
920
921             // Set the status of the radio buttons and the card view backgrounds.
922             if (savedSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
923                 // Check the saved SSL certificate radio button.
924                 savedSslCertificateRadioButton.setChecked(true);
925
926                 // Uncheck the current website SSL certificate radio button.
927                 currentWebsiteCertificateRadioButton.setChecked(false);
928
929                 // Darken the background of the current website SSL certificate linear layout according to the theme.
930                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
931                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
932                 } else {
933                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
934                 }
935             } else if (currentSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
936                 // Check the current website SSL certificate radio button.
937                 currentWebsiteCertificateRadioButton.setChecked(true);
938
939                 // Uncheck the saved SSL certificate radio button.
940                 savedSslCertificateRadioButton.setChecked(false);
941             } else {  // Neither SSL certificate is visible.
942                 // Uncheck both radio buttons.
943                 savedSslCertificateRadioButton.setChecked(false);
944                 currentWebsiteCertificateRadioButton.setChecked(false);
945             }
946         } else {  // An SSL certificate is not pinned.
947             // Hide the SSl certificates and instructions.
948             savedSslCardView.setVisibility(View.GONE);
949             currentSslCardView.setVisibility(View.GONE);
950             noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
951
952             // Uncheck the radio buttons.
953             savedSslCertificateRadioButton.setChecked(false);
954             currentWebsiteCertificateRadioButton.setChecked(false);
955         }
956
957         // Populate the saved and current IP addresses.
958         savedIpAddressesTextView.setText(savedIpAddresses);
959         currentIpAddressesTextView.setText(DomainsActivity.currentIpAddresses);
960
961         // Set the initial display status of the IP addresses card views.
962         if (pinnedIpAddressesSwitch.isChecked()) {  // IP addresses are pinned.
963             // Set the visibility of the saved IP addresses.
964             if (savedIpAddresses == null) {  // There are no saved IP addresses.
965                 savedIpAddressesCardView.setVisibility(View.GONE);
966             } else {  // There are saved IP addresses.
967                 savedIpAddressesCardView.setVisibility(View.VISIBLE);
968             }
969
970             // Set the visibility of the current IP addresses.
971             currentIpAddressesCardView.setVisibility(View.VISIBLE);
972
973             // Set the status of the radio buttons and the card view backgrounds.
974             if (savedIpAddressesCardView.getVisibility() == View.VISIBLE) {  // The saved IP addresses are displayed.
975                 // Check the saved IP addresses radio button.
976                 savedIpAddressesRadioButton.setChecked(true);
977
978                 // Uncheck the current IP addresses radio button.
979                 currentIpAddressesRadioButton.setChecked(false);
980
981                 // Darken the background of the current IP addresses linear layout according to the theme.
982                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
983                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
984                 } else {
985                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
986                 }
987             } else {  // The saved IP addresses are hidden.
988                 // Check the current IP addresses radio button.
989                 currentIpAddressesRadioButton.setChecked(true);
990
991                 // Uncheck the saved IP addresses radio button.
992                 savedIpAddressesRadioButton.setChecked(false);
993             }
994         } else {  // IP addresses are not pinned.
995             // Hide the IP addresses card views.
996             savedIpAddressesCardView.setVisibility(View.GONE);
997             currentIpAddressesCardView.setVisibility(View.GONE);
998
999             // Uncheck the radio buttons.
1000             savedIpAddressesRadioButton.setChecked(false);
1001             currentIpAddressesRadioButton.setChecked(false);
1002         }
1003
1004
1005         // Set the JavaScript switch listener.
1006         javaScriptSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1007             // Update the JavaScript icon.
1008             if (isChecked)
1009                 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null));
1010             else
1011                 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null));
1012
1013             // Set the DOM storage switch status.
1014             domStorageSwitch.setEnabled(isChecked);
1015
1016             // Set the DOM storage ghosted icon status.
1017             domStorageImageView.setEnabled(isChecked);
1018         });
1019
1020         // Set the cookies switch listener.
1021         cookiesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1022             // Update the icon color.
1023             cookiesImageView.setSelected(isChecked);
1024         });
1025
1026         // Set the DOM Storage switch listener.
1027         domStorageSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1028             // Update the icon color.
1029             domStorageImageView.setSelected(isChecked);
1030         });
1031
1032         // Set the form data switch listener.  It can be removed once the minimum API >= 26.
1033         if (Build.VERSION.SDK_INT < 26) {
1034             formDataSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1035                 // Update the icon color.
1036                 formDataImageView.setSelected(isChecked);
1037             });
1038         }
1039
1040         // Set the EasyList switch listener.
1041         easyListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1042             // Update the icon color.
1043             easyListImageView.setSelected(isChecked);
1044         });
1045
1046         // Set the EasyPrivacy switch listener.
1047         easyPrivacySwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1048             // Update the icon color.
1049             easyPrivacyImageView.setSelected(isChecked);
1050         });
1051
1052         // Set the Fanboy's Annoyance List switch listener.
1053         fanboysAnnoyanceListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1054             // Update the icon color.
1055             fanboysAnnoyanceListImageView.setSelected(isChecked);
1056
1057
1058             // Set Fanboy's Social Blocking List switch position.
1059             fanboysSocialBlockingListSwitch.setEnabled(!isChecked);
1060
1061             // Set the Social Blocking List icon ghosted status.
1062             fanboysSocialBlockingListImageView.setEnabled(!isChecked);
1063         });
1064
1065         // Set the Fanboy's Social Blocking List switch listener.
1066         fanboysSocialBlockingListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1067             // Update the icon color.
1068             fanboysSocialBlockingListImageView.setSelected(isChecked);
1069         });
1070
1071         // Set the UltraList switch listener.
1072         ultraListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1073             // Update the icon color.
1074             ultraListImageView.setSelected(isChecked);
1075         });
1076
1077         // Set the UltraPrivacy switch listener.
1078         ultraPrivacySwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1079             // Update the icon color.
1080             ultraPrivacyImageView.setSelected(isChecked);
1081         });
1082
1083         // Set the block all third-party requests switch listener.
1084         blockAllThirdPartyRequestsSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1085             // Update the icon color.
1086             blockAllThirdPartyRequestsImageView.setSelected(isChecked);
1087         });
1088
1089         // Set the user agent spinner listener.
1090         userAgentSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1091             @Override
1092             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1093                 // Set the new user agent.
1094                 switch (position) {
1095                     case MainWebViewActivity.DOMAINS_SYSTEM_DEFAULT_USER_AGENT:
1096                         // Show the user agent TextView.
1097                         userAgentTextView.setVisibility(View.VISIBLE);
1098
1099                         // Hide the custom user agent EditText.
1100                         customUserAgentEditText.setVisibility(View.GONE);
1101
1102                         // Set the user text.
1103                         switch (defaultUserAgentArrayPosition) {
1104                             case MainWebViewActivity.UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
1105                                 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
1106                                 userAgentTextView.setText(defaultUserAgentName);
1107                                 break;
1108
1109                             case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
1110                                 // Display the `WebView` default user agent.
1111                                 userAgentTextView.setText(webViewDefaultUserAgentString);
1112                                 break;
1113
1114                             case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
1115                                 // Display the custom user agent.
1116                                 userAgentTextView.setText(defaultCustomUserAgentString);
1117                                 break;
1118
1119                             default:
1120                                 // Get the user agent string from the user agent data array.
1121                                 userAgentTextView.setText(userAgentDataArray[defaultUserAgentArrayPosition]);
1122                         }
1123                         break;
1124
1125                     case MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT:
1126                         // Show the user agent TextView and set the text.
1127                         userAgentTextView.setVisibility(View.VISIBLE);
1128                         userAgentTextView.setText(webViewDefaultUserAgentString);
1129
1130                         // Hide the custom user agent EditTex.
1131                         customUserAgentEditText.setVisibility(View.GONE);
1132                         break;
1133
1134                     case MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT:
1135                         // Hide the user agent TextView.
1136                         userAgentTextView.setVisibility(View.GONE);
1137
1138                         // Show the custom user agent EditText and set the current user agent name as the text.
1139                         customUserAgentEditText.setVisibility(View.VISIBLE);
1140                         customUserAgentEditText.setText(currentUserAgentName);
1141                         break;
1142
1143                     default:
1144                         // 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.
1145                         userAgentTextView.setVisibility(View.VISIBLE);
1146                         userAgentTextView.setText(userAgentDataArray[position - 1]);
1147
1148                         // Hide `customUserAgentEditText`.
1149                         customUserAgentEditText.setVisibility(View.GONE);
1150                 }
1151             }
1152
1153             @Override
1154             public void onNothingSelected(AdapterView<?> parent) {
1155                 // Do nothing.
1156             }
1157         });
1158
1159         // Set the X-Requested-With header spinner listener.
1160         xRequestedWithHeaderSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1161             @Override
1162             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1163                 // Update the icon and the visibility of the text view.
1164                 switch (position) {
1165                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1166                         // Set the icon color.
1167                         xRequestedWithHeaderImageView.setSelected(defaultXRequestedWithHeader);
1168
1169                         // Show the X-Requested-With header text view.
1170                         xRequestedWithHeaderTextView.setVisibility(View.VISIBLE);
1171                         break;
1172
1173                     case DomainsDatabaseHelper.ENABLED:
1174                         // Set the icon color.
1175                         xRequestedWithHeaderImageView.setSelected(true);
1176
1177                         // Hide the X-Requested-With header text view.
1178                         xRequestedWithHeaderTextView.setVisibility(View.GONE);
1179                         break;
1180
1181                     case DomainsDatabaseHelper.DISABLED:
1182                         // Set the icon color.
1183                         xRequestedWithHeaderImageView.setSelected(false);
1184
1185                         // Hide the X-Requested-With header text view.
1186                         xRequestedWithHeaderTextView.setVisibility(View.GONE);
1187                         break;
1188                 }
1189             }
1190
1191             @Override
1192             public void onNothingSelected(AdapterView<?> parent) {
1193                 // Do nothing.
1194             }
1195         });
1196
1197         // Set the font size spinner listener.
1198         fontSizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1199             @Override
1200             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1201                 // Update the font size display options.
1202                 if (position == 0) {  // The system default font size has been selected.
1203                     // Show the default font size text view.
1204                     defaultFontSizeTextView.setVisibility(View.VISIBLE);
1205
1206                     // Hide the custom font size edit text.
1207                     customFontSizeEditText.setVisibility(View.GONE);
1208                 } else {  // A custom font size has been selected.
1209                     // Hide the default font size text view.
1210                     defaultFontSizeTextView.setVisibility(View.GONE);
1211
1212                     // Show the custom font size edit text.
1213                     customFontSizeEditText.setVisibility(View.VISIBLE);
1214                 }
1215             }
1216
1217             @Override
1218             public void onNothingSelected(AdapterView<?> parent) {
1219                 // Do nothing.
1220             }
1221         });
1222
1223         // Set the swipe to refresh spinner listener.
1224         swipeToRefreshSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1225             @Override
1226             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1227                 // Update the icon and the visibility of the text view.
1228                 switch (position) {
1229                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1230                         // Set the icon color.
1231                         swipeToRefreshImageView.setSelected(defaultSwipeToRefresh);
1232
1233                         // Show the swipe to refresh text view.
1234                         swipeToRefreshTextView.setVisibility(View.VISIBLE);
1235                         break;
1236
1237                     case DomainsDatabaseHelper.ENABLED:
1238                         // Set the icon color.
1239                         swipeToRefreshImageView.setSelected(true);
1240
1241                         // Hide the swipe to refresh text view.
1242                         swipeToRefreshTextView.setVisibility(View.GONE);
1243                         break;
1244
1245                     case DomainsDatabaseHelper.DISABLED:
1246                         // Set the icon color.
1247                         swipeToRefreshImageView.setSelected(false);
1248
1249                         // Hide the swipe to refresh text view.
1250                         swipeToRefreshTextView.setVisibility(View.GONE);
1251                 }
1252             }
1253
1254             @Override
1255             public void onNothingSelected(AdapterView<?> parent) {
1256                 // Do nothing.
1257             }
1258         });
1259
1260         // Set the WebView theme spinner listener.
1261         webViewThemeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1262             @Override
1263             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1264                 // Update the icon and the visibility of the WebView theme text view.
1265                 switch (position) {
1266                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // the domain WebView theme is system default.
1267                         // Set the icon according to the app WebView theme.
1268                         switch (appWebViewThemeEntryNumber) {
1269                             case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // The default WebView theme is system default.
1270                                 // Set the icon color.
1271                                 webViewThemeImageView.setSelected(currentThemeStatus == Configuration.UI_MODE_NIGHT_NO);
1272                                 break;
1273
1274                             case DomainsDatabaseHelper.LIGHT_THEME:  // The default WebView theme is light.
1275                                 // Set the icon color.
1276                                 webViewThemeImageView.setSelected(true);
1277                                 break;
1278
1279                             case DomainsDatabaseHelper.DARK_THEME:  // The default WebView theme is dark.
1280                                 // Set the icon.
1281                                 webViewThemeImageView.setSelected(false);
1282                                 break;
1283                         }
1284
1285                         // Show the WebView theme text view.
1286                         webViewThemeTextView.setVisibility(View.VISIBLE);
1287                         break;
1288
1289                     case DomainsDatabaseHelper.LIGHT_THEME:  // The domain WebView theme is light.
1290                         // Set the icon color.
1291                         webViewThemeImageView.setSelected(true);
1292
1293                         // Hide the WebView theme text view.
1294                         webViewThemeTextView.setVisibility(View.GONE);
1295                         break;
1296
1297                     case DomainsDatabaseHelper.DARK_THEME:  // The domain WebView theme is dark.
1298                         // Set the icon color.
1299                         webViewThemeImageView.setSelected(false);
1300
1301                         // Hide the WebView theme text view.
1302                         webViewThemeTextView.setVisibility(View.GONE);
1303                         break;
1304                 }
1305             }
1306
1307             @Override
1308             public void onNothingSelected(AdapterView<?> parent) {
1309                 // Do nothing.
1310             }
1311         });
1312
1313         // Set the wide viewport spinner listener.
1314         wideViewportSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1315             @Override
1316             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1317                 // Update the icon and the visibility of the wide viewport text view.
1318                 switch (position) {
1319                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1320                         // Set the icon color.
1321                         wideViewportImageView.setSelected(defaultWideViewport);
1322
1323                         // Show the wide viewport text view.
1324                         wideViewportTextView.setVisibility(View.VISIBLE);
1325                         break;
1326
1327                     case DomainsDatabaseHelper.ENABLED:
1328                         // Set the icon color.
1329                         wideViewportImageView.setSelected(true);
1330
1331                         // Hide the wide viewport text view.
1332                         wideViewportTextView.setVisibility(View.GONE);
1333                         break;
1334
1335                     case DomainsDatabaseHelper.DISABLED:
1336                         // Set the icon color.
1337                         wideViewportImageView.setSelected(false);
1338
1339                         // Hid ethe wide viewport text view.
1340                         wideViewportTextView.setVisibility(View.GONE);
1341                         break;
1342                 }
1343             }
1344
1345             @Override
1346             public void onNothingSelected(AdapterView<?> parent) {
1347                 // Do nothing.
1348             }
1349         });
1350
1351         // Set the display webpage images spinner listener.
1352         displayWebpageImagesSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1353             @Override
1354             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1355                 // Update the icon and the visibility of the display images text view.
1356                 switch (position) {
1357                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1358                         // Set the icon color.
1359                         displayWebpageImagesImageView.setSelected(defaultDisplayWebpageImages);
1360
1361                         // Show the display images text view.
1362                         displayImagesTextView.setVisibility(View.VISIBLE);
1363                         break;
1364
1365                     case DomainsDatabaseHelper.ENABLED:
1366                         // Set the icon color.
1367                         displayWebpageImagesImageView.setSelected(true);
1368
1369                         // Hide the display images text view.
1370                         displayImagesTextView.setVisibility(View.GONE);
1371                         break;
1372
1373                     case DomainsDatabaseHelper.DISABLED:
1374                         // Set the icon color.
1375                         displayWebpageImagesImageView.setSelected(false);
1376
1377                         // Hide the display images text view.
1378                         displayImagesTextView.setVisibility(View.GONE);
1379                         break;
1380                 }
1381             }
1382
1383             @Override
1384             public void onNothingSelected(AdapterView<?> parent) {
1385                 // Do nothing.
1386             }
1387         });
1388         
1389         // Set the pinned SSL certificate switch listener.
1390         pinnedSslCertificateSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1391             // Update the icon color.
1392             pinnedSslCertificateImageView.setSelected(isChecked);
1393
1394             // Update the views.
1395             if (isChecked) {  // SSL certificate pinning is enabled.
1396                 // Update the visibility of the saved SSL certificate.
1397                 if (savedSslIssuedToCNameString == null) {
1398                     savedSslCardView.setVisibility(View.GONE);
1399                 } else {
1400                     savedSslCardView.setVisibility(View.VISIBLE);
1401                 }
1402
1403                 // Update the visibility of the current website SSL certificate.
1404                 if (DomainsActivity.sslIssuedToCName == null) {
1405                     // Hide the SSL certificate.
1406                     currentSslCardView.setVisibility(View.GONE);
1407
1408                     // Show the instruction.
1409                     noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
1410                 } else {
1411                     // Show the SSL certificate.
1412                     currentSslCardView.setVisibility(View.VISIBLE);
1413
1414                     // Hide the instruction.
1415                     noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1416                 }
1417
1418                 // Set the status of the radio buttons.
1419                 if (savedSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
1420                     // Check the saved SSL certificate radio button.
1421                     savedSslCertificateRadioButton.setChecked(true);
1422
1423                     // Uncheck the current website SSL certificate radio button.
1424                     currentWebsiteCertificateRadioButton.setChecked(false);
1425
1426                     // Set the background of the saved SSL certificate linear layout to be transparent.
1427                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1428
1429                     // Darken the background of the current website SSL certificate linear layout according to the theme.
1430                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1431                         currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1432                     } else {
1433                         currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1434                     }
1435
1436                     // Scroll to the current website SSL certificate card.
1437                     savedSslCardView.getParent().requestChildFocus(savedSslCardView, savedSslCardView);
1438                 } else if (currentSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1439                     // Check the current website SSL certificate radio button.
1440                     currentWebsiteCertificateRadioButton.setChecked(true);
1441
1442                     // Uncheck the saved SSL certificate radio button.
1443                     savedSslCertificateRadioButton.setChecked(false);
1444
1445                     // Set the background of the current website SSL certificate linear layout to be transparent.
1446                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1447
1448                     // Darken the background of the saved SSL certificate linear layout according to the theme.
1449                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1450                         savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1451                     } else {
1452                         savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1453                     }
1454
1455                     // Scroll to the current website SSL certificate card.
1456                     currentSslCardView.getParent().requestChildFocus(currentSslCardView, currentSslCardView);
1457                 } else {  // Neither SSL certificate is visible.
1458                     // Uncheck both radio buttons.
1459                     savedSslCertificateRadioButton.setChecked(false);
1460                     currentWebsiteCertificateRadioButton.setChecked(false);
1461
1462                     // Scroll to the current website SSL certificate card.
1463                     noCurrentWebsiteCertificateTextView.getParent().requestChildFocus(noCurrentWebsiteCertificateTextView, noCurrentWebsiteCertificateTextView);
1464                 }
1465             } else {  // SSL certificate pinning is disabled.
1466                 // Hide the SSl certificates and instructions.
1467                 savedSslCardView.setVisibility(View.GONE);
1468                 currentSslCardView.setVisibility(View.GONE);
1469                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1470
1471                 // Uncheck the radio buttons.
1472                 savedSslCertificateRadioButton.setChecked(false);
1473                 currentWebsiteCertificateRadioButton.setChecked(false);
1474             }
1475         });
1476
1477         savedSslCardView.setOnClickListener((View view) -> {
1478             // Check the saved SSL certificate radio button.
1479             savedSslCertificateRadioButton.setChecked(true);
1480
1481             // Uncheck the current website SSL certificate radio button.
1482             currentWebsiteCertificateRadioButton.setChecked(false);
1483
1484             // Set the background of the saved SSL certificate linear layout to be transparent.
1485             savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1486
1487             // Darken the background of the current website SSL certificate linear layout according to the theme.
1488             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1489                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1490             } else {
1491                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1492             }
1493         });
1494
1495         savedSslCertificateRadioButton.setOnClickListener((View view) -> {
1496             // Check the saved SSL certificate radio button.
1497             savedSslCertificateRadioButton.setChecked(true);
1498
1499             // Uncheck the current website SSL certificate radio button.
1500             currentWebsiteCertificateRadioButton.setChecked(false);
1501
1502             // Set the background of the saved SSL certificate linear layout to be transparent.
1503             savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1504
1505             // Darken the background of the current website SSL certificate linear layout according to the theme.
1506             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1507                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1508             } else {
1509                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1510             }
1511         });
1512
1513         currentSslCardView.setOnClickListener((View view) -> {
1514             // Check the current website SSL certificate radio button.
1515             currentWebsiteCertificateRadioButton.setChecked(true);
1516
1517             // Uncheck the saved SSL certificate radio button.
1518             savedSslCertificateRadioButton.setChecked(false);
1519
1520             // Set the background of the current website SSL certificate linear layout to be transparent.
1521             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1522
1523             // Darken the background of the saved SSL certificate linear layout according to the theme.
1524             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1525                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1526             } else {
1527                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1528             }
1529         });
1530
1531         currentWebsiteCertificateRadioButton.setOnClickListener((View view) -> {
1532             // Check the current website SSL certificate radio button.
1533             currentWebsiteCertificateRadioButton.setChecked(true);
1534
1535             // Uncheck the saved SSL certificate radio button.
1536             savedSslCertificateRadioButton.setChecked(false);
1537
1538             // Set the background of the current website SSL certificate linear layout to be transparent.
1539             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1540
1541             // Darken the background of the saved SSL certificate linear layout according to the theme.
1542             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1543                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1544             } else {
1545                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1546             }
1547         });
1548
1549         // Set the pinned IP addresses switch listener.
1550         pinnedIpAddressesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1551             // Update the icon color.
1552             pinnedIpAddressesImageView.setSelected(isChecked);
1553
1554             // Update the views.
1555             if (isChecked) {  // IP addresses pinning is enabled.
1556                 // Update the visibility of the saved IP addresses card view.
1557                 if (savedIpAddresses == null) {  // There are no saved IP addresses.
1558                     savedIpAddressesCardView.setVisibility(View.GONE);
1559                 } else {  // There are saved IP addresses.
1560                     savedIpAddressesCardView.setVisibility(View.VISIBLE);
1561                 }
1562
1563                 // Show the current IP addresses card view.
1564                 currentIpAddressesCardView.setVisibility(View.VISIBLE);
1565
1566                 // Set the status of the radio buttons.
1567                 if (savedIpAddressesCardView.getVisibility() == View.VISIBLE) {  // The saved IP addresses are visible.
1568                     // Check the saved IP addresses radio button.
1569                     savedIpAddressesRadioButton.setChecked(true);
1570
1571                     // Uncheck the current IP addresses radio button.
1572                     currentIpAddressesRadioButton.setChecked(false);
1573
1574                     // Set the background of the saved IP addresses linear layout to be transparent.
1575                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1576
1577                     // Darken the background of the current IP addresses linear layout according to the theme.
1578                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1579                         currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1580                     } else {
1581                         currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1582                     }
1583                 } else {  // The saved IP addresses are not visible.
1584                     // Check the current IP addresses radio button.
1585                     currentIpAddressesRadioButton.setChecked(true);
1586
1587                     // Uncheck the saved IP addresses radio button.
1588                     savedIpAddressesRadioButton.setChecked(false);
1589
1590                     // Set the background of the current IP addresses linear layout to be transparent.
1591                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1592
1593                     // Darken the background of the saved IP addresses linear layout according to the theme.
1594                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1595                         savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1596                     } else {
1597                         savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1598                     }
1599                 }
1600
1601                 // Scroll to the bottom of the card views.
1602                 currentIpAddressesCardView.getParent().requestChildFocus(currentIpAddressesCardView, currentIpAddressesCardView);
1603             } else {  // IP addresses pinning is disabled.
1604                 // Hide the IP addresses card views.
1605                 savedIpAddressesCardView.setVisibility(View.GONE);
1606                 currentIpAddressesCardView.setVisibility(View.GONE);
1607
1608                 // Uncheck the radio buttons.
1609                 savedIpAddressesRadioButton.setChecked(false);
1610                 currentIpAddressesRadioButton.setChecked(false);
1611             }
1612         });
1613
1614         savedIpAddressesCardView.setOnClickListener((View view) -> {
1615             // Check the saved IP addresses radio button.
1616             savedIpAddressesRadioButton.setChecked(true);
1617
1618             // Uncheck the current website IP addresses radio button.
1619             currentIpAddressesRadioButton.setChecked(false);
1620
1621             // Set the background of the saved IP addresses linear layout to be transparent.
1622             savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1623
1624             // Darken the background of the current IP addresses linear layout according to the theme.
1625             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1626                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1627             } else {
1628                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1629             }
1630         });
1631
1632         savedIpAddressesRadioButton.setOnClickListener((View view) -> {
1633             // Check the saved IP addresses radio button.
1634             savedIpAddressesRadioButton.setChecked(true);
1635
1636             // Uncheck the current website IP addresses radio button.
1637             currentIpAddressesRadioButton.setChecked(false);
1638
1639             // Set the background of the saved IP addresses linear layout to be transparent.
1640             savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1641
1642             // Darken the background of the current IP addresses linear layout according to the theme.
1643             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1644                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1645             } else {
1646                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1647             }
1648         });
1649
1650         currentIpAddressesCardView.setOnClickListener((View view) -> {
1651             // Check the current IP addresses radio button.
1652             currentIpAddressesRadioButton.setChecked(true);
1653
1654             // Uncheck the saved IP addresses radio button.
1655             savedIpAddressesRadioButton.setChecked(false);
1656
1657             // Set the background of the current IP addresses linear layout to be transparent.
1658             currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1659
1660             // Darken the background of the saved IP addresses linear layout according to the theme.
1661             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1662                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1663             } else {
1664                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1665             }
1666         });
1667
1668         currentIpAddressesRadioButton.setOnClickListener((View view) -> {
1669             // Check the current IP addresses radio button.
1670             currentIpAddressesRadioButton.setChecked(true);
1671
1672             // Uncheck the saved IP addresses radio button.
1673             savedIpAddressesRadioButton.setChecked(false);
1674
1675             // Set the background of the current IP addresses linear layout to be transparent.
1676             currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1677
1678             // Darken the background of the saved IP addresses linear layout according to the theme.
1679             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1680                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1681             } else {
1682                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1683             }
1684         });
1685
1686         // Set the scroll Y.
1687         domainSettingsScrollView.post(() -> domainSettingsScrollView.setScrollY(scrollY));
1688
1689         // Return the domain settings view.
1690         return domainSettingsView;
1691     }
1692
1693     private boolean checkDomainNameAgainstCertificate(String domainName, String certificateCommonName) {
1694         // Initialize `domainNamesMatch`.
1695         boolean domainNamesMatch = false;
1696
1697         // Check various wildcard permutations if `domainName` and `certificateCommonName` are not empty.
1698         // `noinspection ConstantCondition` removes Android Studio's incorrect lint warning that `domainName` can never be `null`.
1699         if ((domainName != null) && (certificateCommonName != null)) {
1700             // Check if the domains match.
1701             if (domainName.equals(certificateCommonName)) {
1702                 domainNamesMatch = true;
1703             }
1704
1705             // If `domainName` starts with a wildcard, check the base domain against all the subdomains of `certificateCommonName`.
1706             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2)) {
1707                 // Remove the initial `*.`.
1708                 String baseDomainName = domainName.substring(2);
1709
1710                 // Setup a copy of `certificateCommonName` to test subdomains.
1711                 String certificateCommonNameSubdomain = certificateCommonName;
1712
1713                 // Check all the subdomains in `certificateCommonNameSubdomain` against `baseDomainName`.
1714                 while (!domainNamesMatch && certificateCommonNameSubdomain.contains(".")) {  // Stop checking if we know that `domainNamesMatch` is `true` or if we run out of  `.`.
1715                     // Test the `certificateCommonNameSubdomain` against `baseDomainName`.
1716                     if (certificateCommonNameSubdomain.equals(baseDomainName)) {
1717                         domainNamesMatch = true;
1718                     }
1719
1720                     // Strip out the lowest subdomain of `certificateCommonNameSubdomain`.
1721                     try {
1722                         certificateCommonNameSubdomain = certificateCommonNameSubdomain.substring(certificateCommonNameSubdomain.indexOf(".") + 1);
1723                     } catch (IndexOutOfBoundsException e) {  // `certificateCommonNameSubdomain` ends with `.`.
1724                         certificateCommonNameSubdomain = "";
1725                     }
1726                 }
1727             }
1728
1729             // If `certificateCommonName` starts with a wildcard, check the base common name against all the subdomains of `domainName`.
1730             if (!domainNamesMatch && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
1731                 // Remove the initial `*.`.
1732                 String baseCertificateCommonName = certificateCommonName.substring(2);
1733
1734                 // Setup a copy of `domainName` to test subdomains.
1735                 String domainNameSubdomain = domainName;
1736
1737                 // Check all the subdomains in `domainNameSubdomain` against `baseCertificateCommonName`.
1738                 while (!domainNamesMatch && domainNameSubdomain.contains(".") && (domainNameSubdomain.length() > 2)) {
1739                     // Test the `domainNameSubdomain` against `baseCertificateCommonName`.
1740                     if (domainNameSubdomain.equals(baseCertificateCommonName)) {
1741                         domainNamesMatch = true;
1742                     }
1743
1744                     // Strip out the lowest subdomain of `domainNameSubdomain`.
1745                     try {
1746                         domainNameSubdomain = domainNameSubdomain.substring(domainNameSubdomain.indexOf(".") + 1);
1747                     } catch (IndexOutOfBoundsException e) { // `domainNameSubdomain` ends with `.`.
1748                         domainNameSubdomain = "";
1749                     }
1750                 }
1751             }
1752
1753             // If both names start with a wildcard, check if the root of one contains the root of the other.
1754             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2) && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
1755                 // Remove the wildcards.
1756                 String rootDomainName = domainName.substring(2);
1757                 String rootCertificateCommonName = certificateCommonName.substring(2);
1758
1759                 // Check if one name ends with the contents of the other.  If so, there will be overlap in the their wildcard subdomains.
1760                 if (rootDomainName.endsWith(rootCertificateCommonName) || rootCertificateCommonName.endsWith(rootDomainName)) {
1761                     domainNamesMatch = true;
1762                 }
1763             }
1764         }
1765
1766         return domainNamesMatch;
1767     }
1768 }