]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java
Improve the Night Theme blue button color. https://redmine.stoutner.com/issues/879
[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.  These are used by activities calling this fragment.
67     public static final String DATABASE_ID = "database_id";
68     public static final String SCROLL_Y = "scroll_y";
69
70     // Define the public variables.  `databaseId` is public static so it can be accessed from `DomainsActivity`. It is also used in `onCreate()` and `onCreateView()`.
71     public static int databaseId;
72
73     // Define 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     // The deprecated `getDrawable()` must be used until the minimum API >= 21.
90     @Override
91     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
92         // Inflate `domain_settings_fragment`.  `false` does not attach it to the root `container`.
93         View domainSettingsView = inflater.inflate(R.layout.domain_settings_fragment, container, false);
94
95         // Get handles for the context and the resources.
96         Resources resources = getResources();
97
98         // Get the current theme status.
99         int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
100
101         // Get a handle for the shared preference.
102         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext());
103
104         // Store the default settings.
105         String defaultUserAgentName = sharedPreferences.getString("user_agent", getString(R.string.user_agent_default_value));
106         String defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value));
107         boolean defaultXRequestedWithHeader = sharedPreferences.getBoolean(getString(R.string.x_requested_with_header_key), true);
108         String defaultFontSizeString = sharedPreferences.getString("font_size", getString(R.string.font_size_default_value));
109         boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
110         String defaultWebViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value));
111         boolean defaultWideViewport = sharedPreferences.getBoolean("wide_viewport", true);
112         boolean defaultDisplayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true);
113
114         // Get handles for the views.
115         ScrollView domainSettingsScrollView = domainSettingsView.findViewById(R.id.domain_settings_scrollview);
116         EditText domainNameEditText = domainSettingsView.findViewById(R.id.domain_settings_name_edittext);
117         ImageView javaScriptImageView = domainSettingsView.findViewById(R.id.javascript_imageview);
118         SwitchCompat javaScriptSwitch = domainSettingsView.findViewById(R.id.javascript_switch);
119         ImageView cookiesImageView = domainSettingsView.findViewById(R.id.cookies_imageview);
120         SwitchCompat cookiesSwitch = domainSettingsView.findViewById(R.id.cookies_switch);
121         ImageView domStorageImageView = domainSettingsView.findViewById(R.id.dom_storage_imageview);
122         SwitchCompat domStorageSwitch = domainSettingsView.findViewById(R.id.dom_storage_switch);
123         ImageView formDataImageView = domainSettingsView.findViewById(R.id.form_data_imageview);  // The form data views can be remove once the minimum API >= 26.
124         SwitchCompat formDataSwitch = domainSettingsView.findViewById(R.id.form_data_switch);  // The form data views can be remove once the minimum API >= 26.
125         ImageView easyListImageView = domainSettingsView.findViewById(R.id.easylist_imageview);
126         SwitchCompat easyListSwitch = domainSettingsView.findViewById(R.id.easylist_switch);
127         ImageView easyPrivacyImageView = domainSettingsView.findViewById(R.id.easyprivacy_imageview);
128         SwitchCompat easyPrivacySwitch = domainSettingsView.findViewById(R.id.easyprivacy_switch);
129         ImageView fanboysAnnoyanceListImageView = domainSettingsView.findViewById(R.id.fanboys_annoyance_list_imageview);
130         SwitchCompat fanboysAnnoyanceListSwitch = domainSettingsView.findViewById(R.id.fanboys_annoyance_list_switch);
131         ImageView fanboysSocialBlockingListImageView = domainSettingsView.findViewById(R.id.fanboys_social_blocking_list_imageview);
132         SwitchCompat fanboysSocialBlockingListSwitch = domainSettingsView.findViewById(R.id.fanboys_social_blocking_list_switch);
133         ImageView ultraListImageView = domainSettingsView.findViewById(R.id.ultralist_imageview);
134         SwitchCompat ultraListSwitch = domainSettingsView.findViewById(R.id.ultralist_switch);
135         ImageView ultraPrivacyImageView = domainSettingsView.findViewById(R.id.ultraprivacy_imageview);
136         SwitchCompat ultraPrivacySwitch = domainSettingsView.findViewById(R.id.ultraprivacy_switch);
137         ImageView blockAllThirdPartyRequestsImageView = domainSettingsView.findViewById(R.id.block_all_third_party_requests_imageview);
138         SwitchCompat blockAllThirdPartyRequestsSwitch = domainSettingsView.findViewById(R.id.block_all_third_party_requests_switch);
139         Spinner userAgentSpinner = domainSettingsView.findViewById(R.id.user_agent_spinner);
140         TextView userAgentTextView = domainSettingsView.findViewById(R.id.user_agent_textview);
141         EditText customUserAgentEditText = domainSettingsView.findViewById(R.id.custom_user_agent_edittext);
142         ImageView xRequestedWithHeaderImageView = domainSettingsView.findViewById(R.id.x_requested_with_header_imageview);
143         Spinner xRequestedWithHeaderSpinner = domainSettingsView.findViewById(R.id.x_requested_with_header_spinner);
144         TextView xRequestedWithHeaderTextView = domainSettingsView.findViewById(R.id.x_requested_with_header_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         // Display the font size settings.
539         if (fontSizeInt == 0) {  // `0` is the code for system default font size.
540             // Set the font size to the system default
541             fontSizeSpinner.setSelection(0);
542
543             // Show the default font size text view.
544             defaultFontSizeTextView.setVisibility(View.VISIBLE);
545
546             // Hide the custom font size edit text.
547             customFontSizeEditText.setVisibility(View.GONE);
548
549             // 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.
550             customFontSizeEditText.setText(defaultFontSizeString);
551         } else {  // A custom font size is selected.
552             // Set the spinner to the custom font size.
553             fontSizeSpinner.setSelection(1);
554
555             // Hide the default font size text view.
556             defaultFontSizeTextView.setVisibility(View.GONE);
557
558             // Show the custom font size edit text.
559             customFontSizeEditText.setVisibility(View.GONE);
560
561             // Set the custom font size.
562             customFontSizeEditText.setText(String.valueOf(fontSizeInt));
563         }
564
565         // Initialize the default font size percentage string.
566         String defaultFontSizePercentageString = defaultFontSizeString + "%";
567
568         // Set the default font size text in the text view.
569         defaultFontSizeTextView.setText(defaultFontSizePercentageString);
570
571         // Open the font size spinner when the text view is clicked.
572         defaultFontSizeTextView.setOnClickListener((View v) -> {
573             // Open the user agent spinner.
574             fontSizeSpinner.performClick();
575         });
576
577         // Select the swipe to refresh selection in the spinner.
578         swipeToRefreshSpinner.setSelection(swipeToRefreshInt);
579
580         // Set the swipe to refresh text.
581         if (defaultSwipeToRefresh)
582             swipeToRefreshTextView.setText(swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
583         else
584             swipeToRefreshTextView.setText(swipeToRefreshArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
585
586         // Set the swipe to refresh icon and text view settings.
587         switch (swipeToRefreshInt) {
588             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
589                 // Set the icon color.
590                 swipeToRefreshImageView.setSelected(defaultSwipeToRefresh);
591
592                 // Show the swipe to refresh text view.
593                 swipeToRefreshTextView.setVisibility(View.VISIBLE);
594                 break;
595
596             case DomainsDatabaseHelper.ENABLED:
597                 // Set the icon color.
598                 swipeToRefreshImageView.setSelected(true);
599
600                 // Hide the swipe to refresh text view.
601                 swipeToRefreshTextView.setVisibility(View.GONE);
602                 break;
603
604             case DomainsDatabaseHelper.DISABLED:
605                 // Set the icon color.
606                 swipeToRefreshImageView.setSelected(false);
607
608                 // Hide the swipe to refresh text view.
609                 swipeToRefreshTextView.setVisibility(View.GONE);
610                 break;
611         }
612
613         // Open the swipe to refresh spinner when the text view is clicked.
614         swipeToRefreshTextView.setOnClickListener((View v) -> {
615             // Open the swipe to refresh spinner.
616             swipeToRefreshSpinner.performClick();
617         });
618
619         // Get the WebView theme string arrays.
620         String[] webViewThemeStringArray = resources.getStringArray(R.array.webview_theme_array);
621         String[] webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values);
622
623         // Define an app WebView theme entry number.
624         int appWebViewThemeEntryNumber;
625
626         // 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.
627         if (defaultWebViewTheme.equals(webViewThemeEntryValuesStringArray[1])) {  // The light theme is selected.
628             // Store the default WebView theme entry number.
629             appWebViewThemeEntryNumber = 1;
630         } else if (defaultWebViewTheme.equals(webViewThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
631             // Store the default WebView theme entry number.
632             appWebViewThemeEntryNumber = 2;
633         } else {  // The system default theme is selected.
634             // Store the default WebView theme entry number.
635             appWebViewThemeEntryNumber = 0;
636         }
637
638         // Select the WebView theme in the spinner.
639         webViewThemeSpinner.setSelection(webViewThemeInt);
640
641         // Set the WebView theme text.
642         if (appWebViewThemeEntryNumber == DomainsDatabaseHelper.SYSTEM_DEFAULT) {  // The app WebView theme is system default.
643             // Set the text according to the current UI theme.
644             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
645                 webViewThemeTextView.setText(webViewThemeStringArray[DomainsDatabaseHelper.LIGHT_THEME]);
646             } else {
647                 webViewThemeTextView.setText(webViewThemeStringArray[DomainsDatabaseHelper.DARK_THEME]);
648             }
649         } else {  // The app WebView theme is not system default.
650             // Set the text according to the app WebView theme.
651             webViewThemeTextView.setText(webViewThemeStringArray[appWebViewThemeEntryNumber]);
652         }
653
654         // Set the WebView theme icon and text visibility.
655         switch (webViewThemeInt) {
656             case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // The domain WebView theme is system default.
657                 // Set the icon according to the app WebView theme.
658                 switch (appWebViewThemeEntryNumber) {
659                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // The default WebView theme is system default.
660                         // Set the icon color.
661                         webViewThemeImageView.setSelected(currentThemeStatus == Configuration.UI_MODE_NIGHT_NO);
662                         break;
663
664                     case DomainsDatabaseHelper.LIGHT_THEME:  // the default WebView theme is light.
665                         // Set the icon color.
666                         webViewThemeImageView.setSelected(true);
667                         break;
668
669                     case DomainsDatabaseHelper.DARK_THEME:  // the default WebView theme is dark.
670                         // Set the icon color.
671                         webViewThemeImageView.setSelected(false);
672                         break;
673                 }
674
675                 // Show the WebView theme text view.
676                 webViewThemeTextView.setVisibility(View.VISIBLE);
677                 break;
678
679             case DomainsDatabaseHelper.LIGHT_THEME:  // The domain WebView theme is light.
680                 // Set the icon color.
681                 webViewThemeImageView.setSelected(true);
682
683                 // Hide the WebView theme text view.
684                 webViewThemeTextView.setVisibility(View.GONE);
685                 break;
686
687             case DomainsDatabaseHelper.DARK_THEME:  // The domain WebView theme is dark.
688                 // Set the icon color.
689                 webViewThemeImageView.setSelected(false);
690
691                 // Hide the WebView theme text view.
692                 webViewThemeTextView.setVisibility(View.GONE);
693                 break;
694         }
695
696         // Open the WebView theme spinner when the text view is clicked.
697         webViewThemeTextView.setOnClickListener((View v) -> {
698             // Open the WebView theme spinner.
699             webViewThemeSpinner.performClick();
700         });
701
702         // Select the wide viewport in the spinner.
703         wideViewportSpinner.setSelection(wideViewportInt);
704
705         // Set the default wide viewport text.
706         if (defaultWideViewport)
707             wideViewportTextView.setText(wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
708         else
709             wideViewportTextView.setText(wideViewportArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
710
711         // Set the wide viewport icon and text view settings.
712         switch (wideViewportInt) {
713             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
714                 // Set the icon color.
715                 wideViewportImageView.setSelected(defaultWideViewport);
716
717                 // Show the wide viewport text view.
718                 wideViewportTextView.setVisibility(View.VISIBLE);
719                 break;
720
721             case DomainsDatabaseHelper.ENABLED:
722                 // Set the icon color.
723                 wideViewportImageView.setSelected(true);
724
725                 // Hide the wide viewport text view.
726                 wideViewportTextView.setVisibility(View.GONE);
727                 break;
728
729             case DomainsDatabaseHelper.DISABLED:
730                 // Set the icon color.
731                 wideViewportImageView.setSelected(false);
732
733                 // Hide the wide viewport text view.
734                 wideViewportTextView.setVisibility(View.GONE);
735                 break;
736         }
737
738         // Open the wide viewport spinner when the text view is clicked.
739         wideViewportTextView.setOnClickListener((View view) -> {
740             // Open the wide viewport spinner.
741             wideViewportSpinner.performClick();
742         });
743
744         // Display the website images mode in the spinner.
745         displayWebpageImagesSpinner.setSelection(displayImagesInt);
746
747         // Set the default display images text.
748         if (defaultDisplayWebpageImages) {
749             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.ENABLED));
750         } else {
751             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISABLED));
752         }
753
754         // Set the display website images icon and text view settings.
755         switch (displayImagesInt) {
756             case DomainsDatabaseHelper.SYSTEM_DEFAULT:
757                 // Set the icon color.
758                 displayWebpageImagesImageView.setSelected(defaultDisplayWebpageImages);
759
760                 // Show the display images text view.
761                 displayImagesTextView.setVisibility(View.VISIBLE);
762                 break;
763
764             case DomainsDatabaseHelper.ENABLED:
765                 // Set the icon color.
766                 displayWebpageImagesImageView.setSelected(true);
767
768                 // Hide the display images text view.
769                 displayImagesTextView.setVisibility(View.GONE);
770                 break;
771
772             case DomainsDatabaseHelper.DISABLED:
773                 // Set the icon color.
774                 displayWebpageImagesImageView.setSelected(false);
775
776                 // Hide the display images text view.
777                 displayImagesTextView.setVisibility(View.GONE);
778                 break;
779         }
780
781         // Open the display images spinner when the text view is clicked.
782         displayImagesTextView.setOnClickListener((View view) -> {
783             // Open the user agent spinner.
784             displayWebpageImagesSpinner.performClick();
785         });
786
787         // Store the current date.
788         Date currentDate = Calendar.getInstance().getTime();
789
790         // Setup the string builders to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
791         savedSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
792         savedSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
793         savedSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
794         savedSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
795         savedSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
796
797         // Check the certificate Common Name against the domain name.
798         boolean savedSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslIssuedToCNameString);
799
800         // Format the issued to Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
801         if (savedSslCommonNameMatchesDomainName) {
802             savedSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
803         } else {
804             savedSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), savedSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
805         }
806
807         //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
808         if ((savedSslStartDate != null) && savedSslStartDate.after(currentDate)) {  // The certificate start date is in the future.
809             savedSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), savedSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
810         } else {  // The certificate start date is in the past.
811             savedSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), savedSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
812         }
813
814         // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
815         if ((savedSslEndDate != null) && savedSslEndDate.before(currentDate)) {  // The certificate end date is in the past.
816             savedSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), savedSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
817         } else {  // The certificate end date is in the future.
818             savedSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), savedSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
819         }
820
821         // Display the saved website SSL certificate strings.
822         savedSslIssuedToCNameTextView.setText(savedSslIssuedToCNameStringBuilder);
823         savedSslIssuedToONameTextView.setText(savedSslIssuedToONameStringBuilder);
824         savedSslIssuedToUNameTextView.setText(savedSslIssuedToUNameStringBuilder);
825         savedSslIssuedByCNameTextView.setText(savedSslIssuedByCNameStringBuilder);
826         savedSslIssuedByONameTextView.setText(savedSslIssuedByONameStringBuilder);
827         savedSslIssuedByUNameTextView.setText(savedSslIssuedByUNameStringBuilder);
828         savedSslStartDateTextView.setText(savedSslStartDateStringBuilder);
829         savedSslEndDateTextView.setText(savedSslEndDateStringBuilder);
830
831         // Populate the current website SSL certificate if there is one.
832         if (DomainsActivity.sslIssuedToCName != null) {
833             // Get dates from the raw long values.
834             Date currentSslStartDate = new Date(DomainsActivity.sslStartDateLong);
835             Date currentSslEndDate = new Date(DomainsActivity.sslEndDateLong);
836
837             // Create a spannable string builder for each text view that needs multiple colors of text.
838             SpannableStringBuilder currentSslIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedToCName);
839             SpannableStringBuilder currentSslIssuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedToOName);
840             SpannableStringBuilder currentSslIssuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedToUName);
841             SpannableStringBuilder currentSslIssuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + DomainsActivity.sslIssuedByCName);
842             SpannableStringBuilder currentSslIssuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + DomainsActivity.sslIssuedByOName);
843             SpannableStringBuilder currentSslIssuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + DomainsActivity.sslIssuedByUName);
844             SpannableStringBuilder currentSslStartDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
845                     .format(currentSslStartDate));
846             SpannableStringBuilder currentSslEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
847                     .format(currentSslEndDate));
848
849             // Setup the string builders to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
850             currentSslIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentSslIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
851             currentSslIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentSslIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
852             currentSslIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
853             currentSslIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentSslIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
854             currentSslIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentSslIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
855
856             // Check the certificate Common Name against the domain name.
857             boolean currentSslCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, DomainsActivity.sslIssuedToCName);
858
859             // Format the issued to Common Name color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
860             if (currentSslCommonNameMatchesDomainName) {
861                 currentSslIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
862             } else {
863                 currentSslIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), currentSslIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
864             }
865
866             //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
867             if (currentSslStartDate.after(currentDate)) {  // The certificate start date is in the future.
868                 currentSslStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), currentSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
869             } else {  // The certificate start date is in the past.
870                 currentSslStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), currentSslStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
871             }
872
873             // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
874             if (currentSslEndDate.before(currentDate)) {  // The certificate end date is in the past.
875                 currentSslEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), currentSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
876             } else {  // The certificate end date is in the future.
877                 currentSslEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), currentSslEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
878             }
879
880             // Display the current website SSL certificate strings.
881             currentSslIssuedToCNameTextView.setText(currentSslIssuedToCNameStringBuilder);
882             currentSslIssuedToONameTextView.setText(currentSslIssuedToONameStringBuilder);
883             currentSslIssuedToUNameTextView.setText(currentSslIssuedToUNameStringBuilder);
884             currentSslIssuedByCNameTextView.setText(currentSslIssuedByCNameStringBuilder);
885             currentSslIssuedByONameTextView.setText(currentSslIssuedByONameStringBuilder);
886             currentSslIssuedByUNameTextView.setText(currentSslIssuedByUNameStringBuilder);
887             currentSslStartDateTextView.setText(currentSslStartDateStringBuilder);
888             currentSslEndDateTextView.setText(currentSslEndDateStringBuilder);
889         }
890
891         // Set the initial display status of the SSL certificates card views.
892         if (pinnedSslCertificateSwitch.isChecked()) {  // An SSL certificate is pinned.
893             // Set the visibility of the saved SSL certificate.
894             if (savedSslIssuedToCNameString == null) {
895                 savedSslCardView.setVisibility(View.GONE);
896             } else {
897                 savedSslCardView.setVisibility(View.VISIBLE);
898             }
899
900             // Set the visibility of the current website SSL certificate.
901             if (DomainsActivity.sslIssuedToCName == null) {  // There is no current SSL certificate.
902                 // Hide the SSL certificate.
903                 currentSslCardView.setVisibility(View.GONE);
904
905                 // Show the instruction.
906                 noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
907             } else {  // There is a current SSL certificate.
908                 // Show the SSL certificate.
909                 currentSslCardView.setVisibility(View.VISIBLE);
910
911                 // Hide the instruction.
912                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
913             }
914
915             // Set the status of the radio buttons and the card view backgrounds.
916             if (savedSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
917                 // Check the saved SSL certificate radio button.
918                 savedSslCertificateRadioButton.setChecked(true);
919
920                 // Uncheck the current website SSL certificate radio button.
921                 currentWebsiteCertificateRadioButton.setChecked(false);
922
923                 // Darken the background of the current website SSL certificate linear layout according to the theme.
924                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
925                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
926                 } else {
927                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
928                 }
929             } else if (currentSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
930                 // Check the current website SSL certificate radio button.
931                 currentWebsiteCertificateRadioButton.setChecked(true);
932
933                 // Uncheck the saved SSL certificate radio button.
934                 savedSslCertificateRadioButton.setChecked(false);
935             } else {  // Neither SSL certificate is visible.
936                 // Uncheck both radio buttons.
937                 savedSslCertificateRadioButton.setChecked(false);
938                 currentWebsiteCertificateRadioButton.setChecked(false);
939             }
940         } else {  // An SSL certificate is not pinned.
941             // Hide the SSl certificates and instructions.
942             savedSslCardView.setVisibility(View.GONE);
943             currentSslCardView.setVisibility(View.GONE);
944             noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
945
946             // Uncheck the radio buttons.
947             savedSslCertificateRadioButton.setChecked(false);
948             currentWebsiteCertificateRadioButton.setChecked(false);
949         }
950
951         // Populate the saved and current IP addresses.
952         savedIpAddressesTextView.setText(savedIpAddresses);
953         currentIpAddressesTextView.setText(DomainsActivity.currentIpAddresses);
954
955         // Set the initial display status of the IP addresses card views.
956         if (pinnedIpAddressesSwitch.isChecked()) {  // IP addresses are pinned.
957             // Set the visibility of the saved IP addresses.
958             if (savedIpAddresses == null) {  // There are no saved IP addresses.
959                 savedIpAddressesCardView.setVisibility(View.GONE);
960             } else {  // There are saved IP addresses.
961                 savedIpAddressesCardView.setVisibility(View.VISIBLE);
962             }
963
964             // Set the visibility of the current IP addresses.
965             currentIpAddressesCardView.setVisibility(View.VISIBLE);
966
967             // Set the status of the radio buttons and the card view backgrounds.
968             if (savedIpAddressesCardView.getVisibility() == View.VISIBLE) {  // The saved IP addresses are displayed.
969                 // Check the saved IP addresses radio button.
970                 savedIpAddressesRadioButton.setChecked(true);
971
972                 // Uncheck the current IP addresses radio button.
973                 currentIpAddressesRadioButton.setChecked(false);
974
975                 // Darken the background of the current IP addresses linear layout according to the theme.
976                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
977                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
978                 } else {
979                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
980                 }
981             } else {  // The saved IP addresses are hidden.
982                 // Check the current IP addresses radio button.
983                 currentIpAddressesRadioButton.setChecked(true);
984
985                 // Uncheck the saved IP addresses radio button.
986                 savedIpAddressesRadioButton.setChecked(false);
987             }
988         } else {  // IP addresses are not pinned.
989             // Hide the IP addresses card views.
990             savedIpAddressesCardView.setVisibility(View.GONE);
991             currentIpAddressesCardView.setVisibility(View.GONE);
992
993             // Uncheck the radio buttons.
994             savedIpAddressesRadioButton.setChecked(false);
995             currentIpAddressesRadioButton.setChecked(false);
996         }
997
998
999         // Set the JavaScript switch listener.
1000         javaScriptSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1001             // Update the JavaScript icon.
1002             if (isChecked)
1003                 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null));
1004             else
1005                 javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null));
1006
1007             // Set the DOM storage switch status.
1008             domStorageSwitch.setEnabled(isChecked);
1009
1010             // Set the DOM storage ghosted icon status.
1011             domStorageImageView.setEnabled(isChecked);
1012         });
1013
1014         // Set the cookies switch listener.
1015         cookiesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1016             // Update the icon color.
1017             cookiesImageView.setSelected(isChecked);
1018         });
1019
1020         // Set the DOM Storage switch listener.
1021         domStorageSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1022             // Update the icon color.
1023             domStorageImageView.setSelected(isChecked);
1024         });
1025
1026         // Set the form data switch listener.  It can be removed once the minimum API >= 26.
1027         if (Build.VERSION.SDK_INT < 26) {
1028             formDataSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1029                 // Update the icon color.
1030                 formDataImageView.setSelected(isChecked);
1031             });
1032         }
1033
1034         // Set the EasyList switch listener.
1035         easyListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1036             // Update the icon color.
1037             easyListImageView.setSelected(isChecked);
1038         });
1039
1040         // Set the EasyPrivacy switch listener.
1041         easyPrivacySwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1042             // Update the icon color.
1043             easyPrivacyImageView.setSelected(isChecked);
1044         });
1045
1046         // Set the Fanboy's Annoyance List switch listener.
1047         fanboysAnnoyanceListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1048             // Update the icon color.
1049             fanboysAnnoyanceListImageView.setSelected(isChecked);
1050
1051
1052             // Set Fanboy's Social Blocking List switch position.
1053             fanboysSocialBlockingListSwitch.setEnabled(!isChecked);
1054
1055             // Set the Social Blocking List icon ghosted status.
1056             fanboysSocialBlockingListImageView.setEnabled(!isChecked);
1057         });
1058
1059         // Set the Fanboy's Social Blocking List switch listener.
1060         fanboysSocialBlockingListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1061             // Update the icon color.
1062             fanboysSocialBlockingListImageView.setSelected(isChecked);
1063         });
1064
1065         // Set the UltraList switch listener.
1066         ultraListSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1067             // Update the icon color.
1068             ultraListImageView.setSelected(isChecked);
1069         });
1070
1071         // Set the UltraPrivacy switch listener.
1072         ultraPrivacySwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1073             // Update the icon color.
1074             ultraPrivacyImageView.setSelected(isChecked);
1075         });
1076
1077         // Set the block all third-party requests switch listener.
1078         blockAllThirdPartyRequestsSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1079             // Update the icon color.
1080             blockAllThirdPartyRequestsImageView.setSelected(isChecked);
1081         });
1082
1083         // Set the user agent spinner listener.
1084         userAgentSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1085             @Override
1086             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1087                 // Set the new user agent.
1088                 switch (position) {
1089                     case MainWebViewActivity.DOMAINS_SYSTEM_DEFAULT_USER_AGENT:
1090                         // Show the user agent TextView.
1091                         userAgentTextView.setVisibility(View.VISIBLE);
1092
1093                         // Hide the custom user agent EditText.
1094                         customUserAgentEditText.setVisibility(View.GONE);
1095
1096                         // Set the user text.
1097                         switch (defaultUserAgentArrayPosition) {
1098                             case MainWebViewActivity.UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
1099                                 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
1100                                 userAgentTextView.setText(defaultUserAgentName);
1101                                 break;
1102
1103                             case MainWebViewActivity.SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
1104                                 // Display the `WebView` default user agent.
1105                                 userAgentTextView.setText(webViewDefaultUserAgentString);
1106                                 break;
1107
1108                             case MainWebViewActivity.SETTINGS_CUSTOM_USER_AGENT:
1109                                 // Display the custom user agent.
1110                                 userAgentTextView.setText(defaultCustomUserAgentString);
1111                                 break;
1112
1113                             default:
1114                                 // Get the user agent string from the user agent data array.
1115                                 userAgentTextView.setText(userAgentDataArray[defaultUserAgentArrayPosition]);
1116                         }
1117                         break;
1118
1119                     case MainWebViewActivity.DOMAINS_WEBVIEW_DEFAULT_USER_AGENT:
1120                         // Show the user agent TextView and set the text.
1121                         userAgentTextView.setVisibility(View.VISIBLE);
1122                         userAgentTextView.setText(webViewDefaultUserAgentString);
1123
1124                         // Hide the custom user agent EditTex.
1125                         customUserAgentEditText.setVisibility(View.GONE);
1126                         break;
1127
1128                     case MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT:
1129                         // Hide the user agent TextView.
1130                         userAgentTextView.setVisibility(View.GONE);
1131
1132                         // Show the custom user agent EditText and set the current user agent name as the text.
1133                         customUserAgentEditText.setVisibility(View.VISIBLE);
1134                         customUserAgentEditText.setText(currentUserAgentName);
1135                         break;
1136
1137                     default:
1138                         // 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.
1139                         userAgentTextView.setVisibility(View.VISIBLE);
1140                         userAgentTextView.setText(userAgentDataArray[position - 1]);
1141
1142                         // Hide `customUserAgentEditText`.
1143                         customUserAgentEditText.setVisibility(View.GONE);
1144                 }
1145             }
1146
1147             @Override
1148             public void onNothingSelected(AdapterView<?> parent) {
1149                 // Do nothing.
1150             }
1151         });
1152
1153         // Set the X-Requested-With header spinner listener.
1154         xRequestedWithHeaderSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1155             @Override
1156             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1157                 // Update the icon and the visibility of the text view.
1158                 switch (position) {
1159                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1160                         // Set the icon color.
1161                         xRequestedWithHeaderImageView.setSelected(defaultXRequestedWithHeader);
1162
1163                         // Show the X-Requested-With header text view.
1164                         xRequestedWithHeaderTextView.setVisibility(View.VISIBLE);
1165                         break;
1166
1167                     case DomainsDatabaseHelper.ENABLED:
1168                         // Set the icon color.
1169                         xRequestedWithHeaderImageView.setSelected(true);
1170
1171                         // Hide the X-Requested-With header text view.
1172                         xRequestedWithHeaderTextView.setVisibility(View.GONE);
1173                         break;
1174
1175                     case DomainsDatabaseHelper.DISABLED:
1176                         // Set the icon color.
1177                         xRequestedWithHeaderImageView.setSelected(false);
1178
1179                         // Hide the X-Requested-With header text view.
1180                         xRequestedWithHeaderTextView.setVisibility(View.GONE);
1181                         break;
1182                 }
1183             }
1184
1185             @Override
1186             public void onNothingSelected(AdapterView<?> parent) {
1187                 // Do nothing.
1188             }
1189         });
1190
1191         // Set the font size spinner listener.
1192         fontSizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1193             @Override
1194             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1195                 // Update the font size display options.
1196                 if (position == 0) {  // The system default font size has been selected.
1197                     // Show the default font size text view.
1198                     defaultFontSizeTextView.setVisibility(View.VISIBLE);
1199
1200                     // Hide the custom font size edit text.
1201                     customFontSizeEditText.setVisibility(View.GONE);
1202                 } else {  // A custom font size has been selected.
1203                     // Hide the default font size text view.
1204                     defaultFontSizeTextView.setVisibility(View.GONE);
1205
1206                     // Show the custom font size edit text.
1207                     customFontSizeEditText.setVisibility(View.VISIBLE);
1208                 }
1209             }
1210
1211             @Override
1212             public void onNothingSelected(AdapterView<?> parent) {
1213                 // Do nothing.
1214             }
1215         });
1216
1217         // Set the swipe to refresh spinner listener.
1218         swipeToRefreshSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1219             @Override
1220             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1221                 // Update the icon and the visibility of the text view.
1222                 switch (position) {
1223                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1224                         // Set the icon color.
1225                         swipeToRefreshImageView.setSelected(defaultSwipeToRefresh);
1226
1227                         // Show the swipe to refresh text view.
1228                         swipeToRefreshTextView.setVisibility(View.VISIBLE);
1229                         break;
1230
1231                     case DomainsDatabaseHelper.ENABLED:
1232                         // Set the icon color.
1233                         swipeToRefreshImageView.setSelected(true);
1234
1235                         // Hide the swipe to refresh text view.
1236                         swipeToRefreshTextView.setVisibility(View.GONE);
1237                         break;
1238
1239                     case DomainsDatabaseHelper.DISABLED:
1240                         // Set the icon color.
1241                         swipeToRefreshImageView.setSelected(false);
1242
1243                         // Hide the swipe to refresh text view.
1244                         swipeToRefreshTextView.setVisibility(View.GONE);
1245                 }
1246             }
1247
1248             @Override
1249             public void onNothingSelected(AdapterView<?> parent) {
1250                 // Do nothing.
1251             }
1252         });
1253
1254         // Set the WebView theme spinner listener.
1255         webViewThemeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1256             @Override
1257             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1258                 // Update the icon and the visibility of the WebView theme text view.
1259                 switch (position) {
1260                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // the domain WebView theme is system default.
1261                         // Set the icon according to the app WebView theme.
1262                         switch (appWebViewThemeEntryNumber) {
1263                             case DomainsDatabaseHelper.SYSTEM_DEFAULT:  // The default WebView theme is system default.
1264                                 // Set the icon color.
1265                                 webViewThemeImageView.setSelected(currentThemeStatus == Configuration.UI_MODE_NIGHT_NO);
1266                                 break;
1267
1268                             case DomainsDatabaseHelper.LIGHT_THEME:  // The default WebView theme is light.
1269                                 // Set the icon color.
1270                                 webViewThemeImageView.setSelected(true);
1271                                 break;
1272
1273                             case DomainsDatabaseHelper.DARK_THEME:  // The default WebView theme is dark.
1274                                 // Set the icon.
1275                                 webViewThemeImageView.setSelected(false);
1276                                 break;
1277                         }
1278
1279                         // Show the WebView theme text view.
1280                         webViewThemeTextView.setVisibility(View.VISIBLE);
1281                         break;
1282
1283                     case DomainsDatabaseHelper.LIGHT_THEME:  // The domain WebView theme is light.
1284                         // Set the icon color.
1285                         webViewThemeImageView.setSelected(true);
1286
1287                         // Hide the WebView theme text view.
1288                         webViewThemeTextView.setVisibility(View.GONE);
1289                         break;
1290
1291                     case DomainsDatabaseHelper.DARK_THEME:  // The domain WebView theme is dark.
1292                         // Set the icon color.
1293                         webViewThemeImageView.setSelected(false);
1294
1295                         // Hide the WebView theme text view.
1296                         webViewThemeTextView.setVisibility(View.GONE);
1297                         break;
1298                 }
1299             }
1300
1301             @Override
1302             public void onNothingSelected(AdapterView<?> parent) {
1303                 // Do nothing.
1304             }
1305         });
1306
1307         // Set the wide viewport spinner listener.
1308         wideViewportSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1309             @Override
1310             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1311                 // Update the icon and the visibility of the wide viewport text view.
1312                 switch (position) {
1313                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1314                         // Set the icon color.
1315                         wideViewportImageView.setSelected(defaultWideViewport);
1316
1317                         // Show the wide viewport text view.
1318                         wideViewportTextView.setVisibility(View.VISIBLE);
1319                         break;
1320
1321                     case DomainsDatabaseHelper.ENABLED:
1322                         // Set the icon color.
1323                         wideViewportImageView.setSelected(true);
1324
1325                         // Hide the wide viewport text view.
1326                         wideViewportTextView.setVisibility(View.GONE);
1327                         break;
1328
1329                     case DomainsDatabaseHelper.DISABLED:
1330                         // Set the icon color.
1331                         wideViewportImageView.setSelected(false);
1332
1333                         // Hid ethe wide viewport text view.
1334                         wideViewportTextView.setVisibility(View.GONE);
1335                         break;
1336                 }
1337             }
1338
1339             @Override
1340             public void onNothingSelected(AdapterView<?> parent) {
1341                 // Do nothing.
1342             }
1343         });
1344
1345         // Set the display webpage images spinner listener.
1346         displayWebpageImagesSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1347             @Override
1348             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1349                 // Update the icon and the visibility of the display images text view.
1350                 switch (position) {
1351                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
1352                         // Set the icon color.
1353                         displayWebpageImagesImageView.setSelected(defaultDisplayWebpageImages);
1354
1355                         // Show the display images text view.
1356                         displayImagesTextView.setVisibility(View.VISIBLE);
1357                         break;
1358
1359                     case DomainsDatabaseHelper.ENABLED:
1360                         // Set the icon color.
1361                         displayWebpageImagesImageView.setSelected(true);
1362
1363                         // Hide the display images text view.
1364                         displayImagesTextView.setVisibility(View.GONE);
1365                         break;
1366
1367                     case DomainsDatabaseHelper.DISABLED:
1368                         // Set the icon color.
1369                         displayWebpageImagesImageView.setSelected(false);
1370
1371                         // Hide the display images text view.
1372                         displayImagesTextView.setVisibility(View.GONE);
1373                         break;
1374                 }
1375             }
1376
1377             @Override
1378             public void onNothingSelected(AdapterView<?> parent) {
1379                 // Do nothing.
1380             }
1381         });
1382         
1383         // Set the pinned SSL certificate switch listener.
1384         pinnedSslCertificateSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1385             // Update the icon color.
1386             pinnedSslCertificateImageView.setSelected(isChecked);
1387
1388             // Update the views.
1389             if (isChecked) {  // SSL certificate pinning is enabled.
1390                 // Update the visibility of the saved SSL certificate.
1391                 if (savedSslIssuedToCNameString == null) {
1392                     savedSslCardView.setVisibility(View.GONE);
1393                 } else {
1394                     savedSslCardView.setVisibility(View.VISIBLE);
1395                 }
1396
1397                 // Update the visibility of the current website SSL certificate.
1398                 if (DomainsActivity.sslIssuedToCName == null) {
1399                     // Hide the SSL certificate.
1400                     currentSslCardView.setVisibility(View.GONE);
1401
1402                     // Show the instruction.
1403                     noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
1404                 } else {
1405                     // Show the SSL certificate.
1406                     currentSslCardView.setVisibility(View.VISIBLE);
1407
1408                     // Hide the instruction.
1409                     noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1410                 }
1411
1412                 // Set the status of the radio buttons.
1413                 if (savedSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
1414                     // Check the saved SSL certificate radio button.
1415                     savedSslCertificateRadioButton.setChecked(true);
1416
1417                     // Uncheck the current website SSL certificate radio button.
1418                     currentWebsiteCertificateRadioButton.setChecked(false);
1419
1420                     // Set the background of the saved SSL certificate linear layout to be transparent.
1421                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1422
1423                     // Darken the background of the current website SSL certificate linear layout according to the theme.
1424                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1425                         currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1426                     } else {
1427                         currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1428                     }
1429
1430                     // Scroll to the current website SSL certificate card.
1431                     savedSslCardView.getParent().requestChildFocus(savedSslCardView, savedSslCardView);
1432                 } else if (currentSslCardView.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1433                     // Check the current website SSL certificate radio button.
1434                     currentWebsiteCertificateRadioButton.setChecked(true);
1435
1436                     // Uncheck the saved SSL certificate radio button.
1437                     savedSslCertificateRadioButton.setChecked(false);
1438
1439                     // Set the background of the current website SSL certificate linear layout to be transparent.
1440                     currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1441
1442                     // Darken the background of the saved SSL certificate linear layout according to the theme.
1443                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1444                         savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1445                     } else {
1446                         savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1447                     }
1448
1449                     // Scroll to the current website SSL certificate card.
1450                     currentSslCardView.getParent().requestChildFocus(currentSslCardView, currentSslCardView);
1451                 } else {  // Neither SSL certificate is visible.
1452                     // Uncheck both radio buttons.
1453                     savedSslCertificateRadioButton.setChecked(false);
1454                     currentWebsiteCertificateRadioButton.setChecked(false);
1455
1456                     // Scroll to the current website SSL certificate card.
1457                     noCurrentWebsiteCertificateTextView.getParent().requestChildFocus(noCurrentWebsiteCertificateTextView, noCurrentWebsiteCertificateTextView);
1458                 }
1459             } else {  // SSL certificate pinning is disabled.
1460                 // Hide the SSl certificates and instructions.
1461                 savedSslCardView.setVisibility(View.GONE);
1462                 currentSslCardView.setVisibility(View.GONE);
1463                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1464
1465                 // Uncheck the radio buttons.
1466                 savedSslCertificateRadioButton.setChecked(false);
1467                 currentWebsiteCertificateRadioButton.setChecked(false);
1468             }
1469         });
1470
1471         savedSslCardView.setOnClickListener((View view) -> {
1472             // Check the saved SSL certificate radio button.
1473             savedSslCertificateRadioButton.setChecked(true);
1474
1475             // Uncheck the current website SSL certificate radio button.
1476             currentWebsiteCertificateRadioButton.setChecked(false);
1477
1478             // Set the background of the saved SSL certificate linear layout to be transparent.
1479             savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1480
1481             // Darken the background of the current website SSL certificate linear layout according to the theme.
1482             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1483                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1484             } else {
1485                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1486             }
1487         });
1488
1489         savedSslCertificateRadioButton.setOnClickListener((View view) -> {
1490             // Check the saved SSL certificate radio button.
1491             savedSslCertificateRadioButton.setChecked(true);
1492
1493             // Uncheck the current website SSL certificate radio button.
1494             currentWebsiteCertificateRadioButton.setChecked(false);
1495
1496             // Set the background of the saved SSL certificate linear layout to be transparent.
1497             savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1498
1499             // Darken the background of the current website SSL certificate linear layout according to the theme.
1500             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1501                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1502             } else {
1503                 currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1504             }
1505         });
1506
1507         currentSslCardView.setOnClickListener((View view) -> {
1508             // Check the current website SSL certificate radio button.
1509             currentWebsiteCertificateRadioButton.setChecked(true);
1510
1511             // Uncheck the saved SSL certificate radio button.
1512             savedSslCertificateRadioButton.setChecked(false);
1513
1514             // Set the background of the current website SSL certificate linear layout to be transparent.
1515             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1516
1517             // Darken the background of the saved SSL certificate linear layout according to the theme.
1518             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1519                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1520             } else {
1521                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1522             }
1523         });
1524
1525         currentWebsiteCertificateRadioButton.setOnClickListener((View view) -> {
1526             // Check the current website SSL certificate radio button.
1527             currentWebsiteCertificateRadioButton.setChecked(true);
1528
1529             // Uncheck the saved SSL certificate radio button.
1530             savedSslCertificateRadioButton.setChecked(false);
1531
1532             // Set the background of the current website SSL certificate linear layout to be transparent.
1533             currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1534
1535             // Darken the background of the saved SSL certificate linear layout according to the theme.
1536             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1537                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1538             } else {
1539                 savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1540             }
1541         });
1542
1543         // Set the pinned IP addresses switch listener.
1544         pinnedIpAddressesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
1545             // Update the icon color.
1546             pinnedIpAddressesImageView.setSelected(isChecked);
1547
1548             // Update the views.
1549             if (isChecked) {  // IP addresses pinning is enabled.
1550                 // Update the visibility of the saved IP addresses card view.
1551                 if (savedIpAddresses == null) {  // There are no saved IP addresses.
1552                     savedIpAddressesCardView.setVisibility(View.GONE);
1553                 } else {  // There are saved IP addresses.
1554                     savedIpAddressesCardView.setVisibility(View.VISIBLE);
1555                 }
1556
1557                 // Show the current IP addresses card view.
1558                 currentIpAddressesCardView.setVisibility(View.VISIBLE);
1559
1560                 // Set the status of the radio buttons.
1561                 if (savedIpAddressesCardView.getVisibility() == View.VISIBLE) {  // The saved IP addresses are visible.
1562                     // Check the saved IP addresses radio button.
1563                     savedIpAddressesRadioButton.setChecked(true);
1564
1565                     // Uncheck the current IP addresses radio button.
1566                     currentIpAddressesRadioButton.setChecked(false);
1567
1568                     // Set the background of the saved IP addresses linear layout to be transparent.
1569                     savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent);
1570
1571                     // Darken the background of the current IP addresses linear layout according to the theme.
1572                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1573                         currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1574                     } else {
1575                         currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1576                     }
1577                 } else {  // The saved IP addresses are not visible.
1578                     // Check the current IP addresses radio button.
1579                     currentIpAddressesRadioButton.setChecked(true);
1580
1581                     // Uncheck the saved IP addresses radio button.
1582                     savedIpAddressesRadioButton.setChecked(false);
1583
1584                     // Set the background of the current IP addresses linear layout to be transparent.
1585                     currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1586
1587                     // Darken the background of the saved IP addresses linear layout according to the theme.
1588                     if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1589                         savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1590                     } else {
1591                         savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1592                     }
1593                 }
1594
1595                 // Scroll to the bottom of the card views.
1596                 currentIpAddressesCardView.getParent().requestChildFocus(currentIpAddressesCardView, currentIpAddressesCardView);
1597             } else {  // IP addresses pinning is disabled.
1598                 // Hide the IP addresses card views.
1599                 savedIpAddressesCardView.setVisibility(View.GONE);
1600                 currentIpAddressesCardView.setVisibility(View.GONE);
1601
1602                 // Uncheck the radio buttons.
1603                 savedIpAddressesRadioButton.setChecked(false);
1604                 currentIpAddressesRadioButton.setChecked(false);
1605             }
1606         });
1607
1608         savedIpAddressesCardView.setOnClickListener((View view) -> {
1609             // Check the saved IP addresses radio button.
1610             savedIpAddressesRadioButton.setChecked(true);
1611
1612             // Uncheck the current website IP addresses radio button.
1613             currentIpAddressesRadioButton.setChecked(false);
1614
1615             // Set the background of the saved IP addresses linear layout to be transparent.
1616             savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1617
1618             // Darken the background of the current IP addresses linear layout according to the theme.
1619             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1620                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1621             } else {
1622                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1623             }
1624         });
1625
1626         savedIpAddressesRadioButton.setOnClickListener((View view) -> {
1627             // Check the saved IP addresses radio button.
1628             savedIpAddressesRadioButton.setChecked(true);
1629
1630             // Uncheck the current website IP addresses radio button.
1631             currentIpAddressesRadioButton.setChecked(false);
1632
1633             // Set the background of the saved IP addresses linear layout to be transparent.
1634             savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1635
1636             // Darken the background of the current IP addresses linear layout according to the theme.
1637             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1638                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1639             } else {
1640                 currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1641             }
1642         });
1643
1644         currentIpAddressesCardView.setOnClickListener((View view) -> {
1645             // Check the current IP addresses radio button.
1646             currentIpAddressesRadioButton.setChecked(true);
1647
1648             // Uncheck the saved IP addresses radio button.
1649             savedIpAddressesRadioButton.setChecked(false);
1650
1651             // Set the background of the current IP addresses linear layout to be transparent.
1652             currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1653
1654             // Darken the background of the saved IP addresses linear layout according to the theme.
1655             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1656                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1657             } else {
1658                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1659             }
1660         });
1661
1662         currentIpAddressesRadioButton.setOnClickListener((View view) -> {
1663             // Check the current IP addresses radio button.
1664             currentIpAddressesRadioButton.setChecked(true);
1665
1666             // Uncheck the saved IP addresses radio button.
1667             savedIpAddressesRadioButton.setChecked(false);
1668
1669             // Set the background of the current IP addresses linear layout to be transparent.
1670             currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent);
1671
1672             // Darken the background of the saved IP addresses linear layout according to the theme.
1673             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
1674                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33);
1675             } else {
1676                 savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11);
1677             }
1678         });
1679
1680         // Set the scroll Y.
1681         domainSettingsScrollView.post(() -> domainSettingsScrollView.setScrollY(scrollY));
1682
1683         // Return the domain settings view.
1684         return domainSettingsView;
1685     }
1686
1687     private boolean checkDomainNameAgainstCertificate(String domainName, String certificateCommonName) {
1688         // Initialize `domainNamesMatch`.
1689         boolean domainNamesMatch = false;
1690
1691         // Check various wildcard permutations if `domainName` and `certificateCommonName` are not empty.
1692         // `noinspection ConstantCondition` removes Android Studio's incorrect lint warning that `domainName` can never be `null`.
1693         if ((domainName != null) && (certificateCommonName != null)) {
1694             // Check if the domains match.
1695             if (domainName.equals(certificateCommonName)) {
1696                 domainNamesMatch = true;
1697             }
1698
1699             // If `domainName` starts with a wildcard, check the base domain against all the subdomains of `certificateCommonName`.
1700             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2)) {
1701                 // Remove the initial `*.`.
1702                 String baseDomainName = domainName.substring(2);
1703
1704                 // Setup a copy of `certificateCommonName` to test subdomains.
1705                 String certificateCommonNameSubdomain = certificateCommonName;
1706
1707                 // Check all the subdomains in `certificateCommonNameSubdomain` against `baseDomainName`.
1708                 while (!domainNamesMatch && certificateCommonNameSubdomain.contains(".")) {  // Stop checking if we know that `domainNamesMatch` is `true` or if we run out of  `.`.
1709                     // Test the `certificateCommonNameSubdomain` against `baseDomainName`.
1710                     if (certificateCommonNameSubdomain.equals(baseDomainName)) {
1711                         domainNamesMatch = true;
1712                     }
1713
1714                     // Strip out the lowest subdomain of `certificateCommonNameSubdomain`.
1715                     try {
1716                         certificateCommonNameSubdomain = certificateCommonNameSubdomain.substring(certificateCommonNameSubdomain.indexOf(".") + 1);
1717                     } catch (IndexOutOfBoundsException e) {  // `certificateCommonNameSubdomain` ends with `.`.
1718                         certificateCommonNameSubdomain = "";
1719                     }
1720                 }
1721             }
1722
1723             // If `certificateCommonName` starts with a wildcard, check the base common name against all the subdomains of `domainName`.
1724             if (!domainNamesMatch && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
1725                 // Remove the initial `*.`.
1726                 String baseCertificateCommonName = certificateCommonName.substring(2);
1727
1728                 // Setup a copy of `domainName` to test subdomains.
1729                 String domainNameSubdomain = domainName;
1730
1731                 // Check all the subdomains in `domainNameSubdomain` against `baseCertificateCommonName`.
1732                 while (!domainNamesMatch && domainNameSubdomain.contains(".") && (domainNameSubdomain.length() > 2)) {
1733                     // Test the `domainNameSubdomain` against `baseCertificateCommonName`.
1734                     if (domainNameSubdomain.equals(baseCertificateCommonName)) {
1735                         domainNamesMatch = true;
1736                     }
1737
1738                     // Strip out the lowest subdomain of `domainNameSubdomain`.
1739                     try {
1740                         domainNameSubdomain = domainNameSubdomain.substring(domainNameSubdomain.indexOf(".") + 1);
1741                     } catch (IndexOutOfBoundsException e) { // `domainNameSubdomain` ends with `.`.
1742                         domainNameSubdomain = "";
1743                     }
1744                 }
1745             }
1746
1747             // If both names start with a wildcard, check if the root of one contains the root of the other.
1748             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2) && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
1749                 // Remove the wildcards.
1750                 String rootDomainName = domainName.substring(2);
1751                 String rootCertificateCommonName = certificateCommonName.substring(2);
1752
1753                 // Check if one name ends with the contents of the other.  If so, there will be overlap in the their wildcard subdomains.
1754                 if (rootDomainName.endsWith(rootCertificateCommonName) || rootCertificateCommonName.endsWith(rootDomainName)) {
1755                     domainNamesMatch = true;
1756                 }
1757             }
1758         }
1759
1760         return domainNamesMatch;
1761     }
1762 }