Add SSL certificate pinning. Implements https://redmine.stoutner.com/issues/54.
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / fragments / DomainSettingsFragment.java
1 /*
2  * Copyright © 2017 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
5  *
6  * Privacy Browser 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 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.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package com.stoutner.privacybrowser.fragments;
21
22 import android.annotation.SuppressLint;
23 import android.content.Context;
24 import android.content.SharedPreferences;
25 import android.content.res.Resources;
26 import android.database.Cursor;
27 import android.net.http.SslCertificate;
28 import android.os.Build;
29 import android.os.Bundle;
30 // We have to use `android.support.v4.app.Fragment` until minimum API >= 23.  Otherwise we cannot call `getContext()`.
31 import android.preference.PreferenceManager;
32 import android.support.v4.app.Fragment;
33 import android.text.Editable;
34 import android.text.SpannableStringBuilder;
35 import android.text.Spanned;
36 import android.text.TextWatcher;
37 import android.text.style.ForegroundColorSpan;
38 import android.view.LayoutInflater;
39 import android.view.View;
40 import android.view.ViewGroup;
41 import android.webkit.WebView;
42 import android.widget.AdapterView;
43 import android.widget.ArrayAdapter;
44 import android.widget.CompoundButton;
45 import android.widget.EditText;
46 import android.widget.ImageView;
47 import android.widget.LinearLayout;
48 import android.widget.RadioButton;
49 import android.widget.Spinner;
50 import android.widget.Switch;
51 import android.widget.TextView;
52
53 import com.stoutner.privacybrowser.R;
54 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
55 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
56
57 import java.text.DateFormat;
58 import java.util.Calendar;
59 import java.util.Date;
60
61 public class DomainSettingsFragment extends Fragment {
62     // `DATABASE_ID` is used by activities calling this fragment.
63     public static final String DATABASE_ID = "database_id";
64
65     // `databaseId` is public static so it can be accessed from `DomainsActivity`. It is also used in `onCreate()` and `onCreateView()`.
66     public static int databaseId;
67
68     @Override
69     public void onCreate(Bundle savedInstanceState) {
70         super.onCreate(savedInstanceState);
71
72         // Store the database id in `databaseId`.
73         databaseId = getArguments().getInt(DATABASE_ID);
74     }
75
76     // We have to use the deprecated `getDrawable()` until the minimum API >= 21.
77     @SuppressWarnings("deprecation")
78     @Override
79     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
80         // Inflate `domain_settings_fragment`.  `false` does not attach it to the root `container`.
81         View domainSettingsView = inflater.inflate(R.layout.domain_settings_fragment, container, false);
82
83         // Get a handle for the `Context` and the `Resources`.
84         Context context = getContext();
85         final Resources resources = getResources();
86
87         // Get a handle for the shared preference.
88         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
89
90         // Store the default user agent string values.
91         final String defaultUserAgentString = sharedPreferences.getString("user_agent", "PrivacyBrowser/1.0");
92         final String defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0");
93         String defaultFontSizeString = sharedPreferences.getString("default_font_size", "100");
94         boolean defaultDisplayWebpageImagesBoolean = sharedPreferences.getBoolean("display_website_images", true);
95
96         // Get handles for the views in the fragment.
97         final EditText domainNameEditText = (EditText) domainSettingsView.findViewById(R.id.domain_settings_name_edittext);
98         Switch javaScriptEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_javascript_switch);
99         final ImageView javaScriptImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_javascript_imageview);
100         Switch firstPartyCookiesEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_first_party_cookies_switch);
101         final ImageView firstPartyCookiesImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_first_party_cookies_imageview);
102         LinearLayout thirdPartyCookiesLinearLayout = (LinearLayout) domainSettingsView.findViewById(R.id.domain_settings_third_party_cookies_linearlayout);
103         final Switch thirdPartyCookiesEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_third_party_cookies_switch);
104         final ImageView thirdPartyCookiesImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_third_party_cookies_imageview);
105         final Switch domStorageEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_dom_storage_switch);
106         final ImageView domStorageImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_dom_storage_imageview);
107         Switch formDataEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_form_data_switch);
108         final ImageView formDataImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_form_data_imageview);
109         Spinner userAgentSpinner = (Spinner) domainSettingsView.findViewById(R.id.domain_settings_user_agent_spinner);
110         final TextView userAgentTextView = (TextView) domainSettingsView.findViewById(R.id.domain_settings_user_agent_textview);
111         final EditText customUserAgentEditText = (EditText) domainSettingsView.findViewById(R.id.domain_settings_custom_user_agent_edittext);
112         Spinner fontSizeSpinner = (Spinner) domainSettingsView.findViewById(R.id.domain_settings_font_size_spinner);
113         final TextView fontSizeTextView = (TextView) domainSettingsView.findViewById(R.id.domain_settings_font_size_textview);
114         final ImageView displayWebpageImagesImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_display_webpage_images_imageview);
115         Spinner displayWebpageImagesSpinner = (Spinner) domainSettingsView.findViewById(R.id.domain_settings_display_webpage_images_spinner);
116         final TextView displayImagesTextView = (TextView) domainSettingsView.findViewById(R.id.domain_settings_display_webpage_images_textview);
117         final ImageView pinnedSslCertificateImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_pinned_ssl_certificate_imageview);
118         Switch pinnedSslCertificateSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_pinned_ssl_certificate_switch);
119         final LinearLayout savedSslCertificateLinearLayout = (LinearLayout) domainSettingsView.findViewById(R.id.saved_ssl_certificate_linearlayout);
120         final RadioButton savedSslCertificateRadioButton = (RadioButton) domainSettingsView.findViewById(R.id.saved_ssl_certificate_radiobutton);
121         final TextView savedSslCertificateIssuedToCNameTextView = (TextView) domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_cname);
122         TextView savedSslCertificateIssuedToONameTextView = (TextView) domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_oname);
123         TextView savedSslCertificateIssuedToUNameTextView = (TextView) domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_to_uname);
124         TextView savedSslCertificateIssuedByCNameTextView = (TextView) domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_cname);
125         TextView savedSslCertificateIssuedByONameTextView = (TextView) domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_oname);
126         TextView savedSslCertificateIssuedByUNameTextView = (TextView) domainSettingsView.findViewById(R.id.saved_ssl_certificate_issued_by_uname);
127         TextView savedSslCertificateStartDateTextView = (TextView) domainSettingsView.findViewById(R.id.saved_ssl_certificate_start_date);
128         TextView savedSslCertificateEndDateTextView = (TextView) domainSettingsView.findViewById(R.id.saved_ssl_certificate_end_date);
129         final LinearLayout currentWebsiteCertificateLinearLayout = (LinearLayout) domainSettingsView.findViewById(R.id.current_website_certificate_linearlayout);
130         final RadioButton currentWebsiteCertificateRadioButton = (RadioButton) domainSettingsView.findViewById(R.id.current_website_certificate_radiobutton);
131         final TextView currentWebsiteCertificateIssuedToCNameTextView = (TextView) domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_cname);
132         TextView currentWebsiteCertificateIssuedToONameTextView = (TextView) domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_oname);
133         TextView currentWebsiteCertificateIssuedToUNameTextView = (TextView) domainSettingsView.findViewById(R.id.current_website_certificate_issued_to_uname);
134         TextView currentWebsiteCertificateIssuedByCNameTextView = (TextView) domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_cname);
135         TextView currentWebsiteCertificateIssuedByONameTextView = (TextView) domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_oname);
136         TextView currentWebsiteCertificateIssuedByUNameTextView = (TextView) domainSettingsView.findViewById(R.id.current_website_certificate_issued_by_uname);
137         TextView currentWebsiteCertificateStartDateTextView = (TextView) domainSettingsView.findViewById(R.id.current_website_certificate_start_date);
138         TextView currentWebsiteCertificateEndDateTextView = (TextView) domainSettingsView.findViewById(R.id.current_website_certificate_end_date);
139         final TextView noCurrentWebsiteCertificateTextView = (TextView) domainSettingsView.findViewById(R.id.no_current_website_certificate);
140
141         // Setup the SSL certificate labels.
142         final String cNameLabel = getString(R.string.common_name) + "  ";
143         String oNameLabel = getString(R.string.organization) + "  ";
144         String uNameLabel = getString(R.string.organizational_unit) + "  ";
145         String startDateLabel = getString(R.string.start_date) + "  ";
146         String endDateLabel = getString(R.string.end_date) + "  ";
147
148         // Get the current website SSL certificate
149         final SslCertificate currentWebsiteSslCertificate = MainWebViewActivity.sslCertificate;
150
151         // Initialize the database handler.  The two `nulls` do not specify the database name or a `CursorFactory`.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
152         DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0);
153
154         // Get the database `Cursor` for this ID and move it to the first row.
155         Cursor domainCursor = domainsDatabaseHelper.getCursorForId(databaseId);
156         domainCursor.moveToFirst();
157
158         // Save the `Cursor` entries as variables.
159         String domainNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME));
160         int javaScriptEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT));
161         int firstPartyCookiesEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES));
162         int thirdPartyCookiesEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES));
163         int domStorageEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE));
164         int formDataEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA));
165         final String currentUserAgentString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
166         int fontSizeInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
167         int displayImagesInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
168         int pinnedSslCertificateInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE));
169         final String savedSslCertificateIssuedToCNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME));
170         String savedSslCertificateIssuedToONameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION));
171         String savedSslCertificateIssuedToUNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT));
172         String savedSslCertificateIssuedByCNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME));
173         String savedSslCertificateIssuedByONameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION));
174         String savedSslCertificateIssuedByUNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT));
175
176         // Initialize the saved SSL certificate date variables.
177         Date savedSslCertificateStartDate = null;
178         Date savedSslCertificateEndDate = null;
179
180         // Only get the saved SSL certificate dates from the cursor if they are not set to `0`.
181         if (domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) != 0) {
182             savedSslCertificateStartDate = new Date(domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)));
183         }
184
185         if (domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) != 0) {
186             savedSslCertificateEndDate = new Date(domainCursor.getLong(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)));
187         }
188
189         // Create `ArrayAdapters` for the `Spinners`and their `entry values`.
190         ArrayAdapter<CharSequence> userAgentArrayAdapter = ArrayAdapter.createFromResource(context, R.array.domain_settings_user_agent_entries, R.layout.spinner_item);
191         final ArrayAdapter<CharSequence> userAgentEntryValuesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.domain_settings_user_agent_entry_values, R.layout.spinner_item);
192         ArrayAdapter<CharSequence> fontSizeArrayAdapter = ArrayAdapter.createFromResource(context, R.array.domain_settings_font_size_entries, R.layout.spinner_item);
193         ArrayAdapter<CharSequence> fontSizeEntryValuesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.domain_settings_font_size_entry_values, R.layout.spinner_item);
194         final ArrayAdapter<CharSequence> displayImagesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.display_website_images_array, R.layout.spinner_item);
195
196         // Set the `DropDownViewResource` on the `Spinners`.
197         userAgentArrayAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
198         fontSizeArrayAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
199         displayImagesArrayAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
200
201         // Set the `ArrayAdapters` for the `Spinners`.
202         userAgentSpinner.setAdapter(userAgentArrayAdapter);
203         fontSizeSpinner.setAdapter(fontSizeArrayAdapter);
204         displayWebpageImagesSpinner.setAdapter(displayImagesArrayAdapter);
205
206         // Create a `SpannableStringBuilder` for each `TextView` that needs multiple colors of text.
207         SpannableStringBuilder savedSslCertificateIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslCertificateIssuedToCNameString);
208         SpannableStringBuilder savedSslCertificateIssuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + savedSslCertificateIssuedToONameString);
209         SpannableStringBuilder savedSslCertificateIssuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + savedSslCertificateIssuedToUNameString);
210         SpannableStringBuilder savedSslCertificateIssuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslCertificateIssuedByCNameString);
211         SpannableStringBuilder savedSslCertificateIssuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + savedSslCertificateIssuedByONameString);
212         SpannableStringBuilder savedSslCertificateIssuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + savedSslCertificateIssuedByUNameString);
213
214         // Initialize the `SpannableStringBuilders` for the SSL certificate dates.
215         SpannableStringBuilder savedSslCertificateStartDateStringBuilder;
216         SpannableStringBuilder savedSslCertificateEndDateStringBuilder;
217
218         // Leave the SSL certificate dates empty if they are `null`.
219         if (savedSslCertificateStartDate == null) {
220             savedSslCertificateStartDateStringBuilder = new SpannableStringBuilder(startDateLabel);
221         } else {
222             savedSslCertificateStartDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslCertificateStartDate));
223         }
224
225         if (savedSslCertificateEndDate == null) {
226             savedSslCertificateEndDateStringBuilder = new SpannableStringBuilder(endDateLabel);
227         } else {
228             savedSslCertificateEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslCertificateEndDate));
229         }
230
231         // Create a red `ForegroundColorSpan`.  We have to use the deprecated `getColor` until API >= 23.
232         final ForegroundColorSpan redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
233
234         // Create a blue `ForegroundColorSpan`.
235         final ForegroundColorSpan blueColorSpan;
236
237         // Set `blueColorSpan` according to the theme.  We have to use the deprecated `getColor()` until API >= 23.
238         if (MainWebViewActivity.darkTheme) {
239             //noinspection deprecation
240             blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_400));
241         } else {
242             //noinspection deprecation
243             blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
244         }
245
246         // Set the domain name from the the database cursor.
247         domainNameEditText.setText(domainNameString);
248
249         // Update the certificates' `Common Name` color when the domain name text changes.
250         domainNameEditText.addTextChangedListener(new TextWatcher() {
251             @Override
252             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
253                 // Do nothing.
254             }
255
256             @Override
257             public void onTextChanged(CharSequence s, int start, int before, int count) {
258                 // Do nothing.
259             }
260
261             @Override
262             public void afterTextChanged(Editable s) {
263                 // Get the new domain name.
264                 String newDomainName = domainNameEditText.getText().toString();
265
266                 // Check the saved SSL certificate against the new domain name.
267                 boolean savedSslCertificateMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, savedSslCertificateIssuedToCNameString);
268
269                 // Create a `SpannableStringBuilder` for the saved certificate `Common Name`.
270                 SpannableStringBuilder savedSslCertificateCommonNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslCertificateIssuedToCNameString);
271
272                 // Format the saved certificate `Common Name` color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
273                 if (savedSslCertificateMatchesNewDomainName) {
274                     savedSslCertificateCommonNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslCertificateCommonNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
275                 } else {
276                     savedSslCertificateCommonNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), savedSslCertificateCommonNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
277                 }
278
279                 // Update `savedSslCertificateIssuedToCNameTextView`.
280                 savedSslCertificateIssuedToCNameTextView.setText(savedSslCertificateCommonNameStringBuilder);
281
282                 // Update the current website certificate if it exists.
283                 if (currentWebsiteSslCertificate != null) {
284                     // Get the current website certificate `Common Name`.
285                     String currentWebsiteCertificateCommonName = currentWebsiteSslCertificate.getIssuedTo().getCName();
286
287                     // Check the current website certificate against the new domain name.
288                     boolean currentWebsiteCertificateMatchesNewDomainName = checkDomainNameAgainstCertificate(newDomainName, currentWebsiteCertificateCommonName);
289
290                     // Create a `SpannableStringBuilder` for the current website certificate `Common Name`.
291                     SpannableStringBuilder currentWebsiteCertificateCommonNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentWebsiteCertificateCommonName);
292
293                     // Format the current certificate `Common Name` color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
294                     if (currentWebsiteCertificateMatchesNewDomainName) {
295                         currentWebsiteCertificateCommonNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentWebsiteCertificateCommonNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
296                     } else {
297                         currentWebsiteCertificateCommonNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), currentWebsiteCertificateCommonNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
298                     }
299
300                     // Update `currentWebsiteCertificateIssuedToCNameTextView`.
301                     currentWebsiteCertificateIssuedToCNameTextView.setText(currentWebsiteCertificateCommonNameStringBuilder);
302                 }
303             }
304         });
305
306         // Set the JavaScript status.
307         if (javaScriptEnabledInt == 1) {  // JavaScript is enabled.
308             javaScriptEnabledSwitch.setChecked(true);
309             javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled));
310         } else {  // JavaScript is disabled.
311             javaScriptEnabledSwitch.setChecked(false);
312             javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode));
313         }
314
315         // Set the first-party cookies status.  Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons.
316         if (firstPartyCookiesEnabledInt == 1) {  // First-party cookies are enabled.
317             firstPartyCookiesEnabledSwitch.setChecked(true);
318             firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_enabled));
319         } else {  // First-party cookies are disabled.
320             firstPartyCookiesEnabledSwitch.setChecked(false);
321
322             // Set the icon according to the theme.
323             if (MainWebViewActivity.darkTheme) {
324                 firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
325             } else {
326                 firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
327             }
328         }
329
330         // Only display third-party cookies if SDK_INT >= 21.
331         if (Build.VERSION.SDK_INT >= 21) {  // Third-party cookies can be configured for API >= 21.
332             // Only enable third-party-cookies if first-party cookies are enabled.
333             if (firstPartyCookiesEnabledInt == 1) {  // First-party cookies are enabled.
334                 // Set the third-party cookies status.  Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons.
335                 if (thirdPartyCookiesEnabledInt == 1) {  // Both first-party and third-party cookies are enabled.
336                     thirdPartyCookiesEnabledSwitch.setChecked(true);
337                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
338                 } else {  // First party cookies are enabled but third-party cookies are disabled.
339                     thirdPartyCookiesEnabledSwitch.setChecked(false);
340
341                     // Set the icon according to the theme.
342                     if (MainWebViewActivity.darkTheme) {
343                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
344                     } else {
345                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
346                     }
347                 }
348             } else {  // First-party cookies are disabled.
349                 // Set the status of third-party cookies.
350                 if (thirdPartyCookiesEnabledInt == 1) {
351                     thirdPartyCookiesEnabledSwitch.setChecked(true);
352                 } else {
353                     thirdPartyCookiesEnabledSwitch.setChecked(false);
354                 }
355
356                 // Disable the third-party cookies switch.
357                 thirdPartyCookiesEnabledSwitch.setEnabled(false);
358
359                 // Set the icon according to the theme.
360                 if (MainWebViewActivity.darkTheme) {
361                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_dark));
362                 } else {
363                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_light));
364                 }
365             }
366         } else {  // Third-party cookies cannot be configured for API <= 21.
367             // Hide the `LinearLayout` for third-party cookies.
368             thirdPartyCookiesLinearLayout.setVisibility(View.GONE);
369         }
370
371         // Only enable DOM storage if JavaScript is enabled.
372         if (javaScriptEnabledInt == 1) {  // JavaScript is enabled.
373             // Set the DOM storage status.  Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons.
374             if (domStorageEnabledInt == 1) {  // Both JavaScript and DOM storage are enabled.
375                 domStorageEnabledSwitch.setChecked(true);
376                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
377             } else {  // JavaScript is enabled but DOM storage is disabled.
378                 // Set the DOM storage switch to off.
379                 domStorageEnabledSwitch.setChecked(false);
380
381                 // Set the icon according to the theme.
382                 if (MainWebViewActivity.darkTheme) {
383                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
384                 } else {
385                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
386                 }
387             }
388         } else {  // JavaScript is disabled.
389             // Set the checked status of DOM storage.
390             if (domStorageEnabledInt == 1) {  // DOM storage is enabled but JavaScript is disabled.
391                 domStorageEnabledSwitch.setChecked(true);
392             } else {  // Both JavaScript and DOM storage are disabled.
393                 domStorageEnabledSwitch.setChecked(false);
394             }
395
396             // Disable `domStorageEnabledSwitch`.
397             domStorageEnabledSwitch.setEnabled(false);
398
399             // Set the icon according to the theme.
400             if (MainWebViewActivity.darkTheme) {
401                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_dark));
402             } else {
403                 domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_light));
404             }
405         }
406
407         // Set the form data status.  Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons.
408         if (formDataEnabledInt == 1) {  // Form data is enabled.
409             formDataEnabledSwitch.setChecked(true);
410             formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_enabled));
411         } else {  // Form data is disabled.
412             // Set the form data switch to off.
413             formDataEnabledSwitch.setChecked(false);
414
415             // Set the icon according to the theme.
416             if (MainWebViewActivity.darkTheme) {
417                 formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_dark));
418             } else {
419                 formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_light));
420             }
421         }
422
423         // We need to inflated a `WebView` to get the default user agent.
424         // `@SuppressLint("InflateParams")` removes the warning about using `null` as the `ViewGroup`, which in this case makes sense because we don't want to display `bare_webview` on the screen.  `false` does not attach the view to the root.
425         @SuppressLint("InflateParams") View bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false);
426         WebView bareWebView = (WebView) bareWebViewLayout.findViewById(R.id.bare_webview);
427         final String webViewDefaultUserAgentString = bareWebView.getSettings().getUserAgentString();
428
429         // Get the position of the user agent in `userAgentEntryValuesArrayAdapter`.
430         int userAgentArrayPosition = userAgentEntryValuesArrayAdapter.getPosition(currentUserAgentString);
431
432         // Set the user agent.
433         if (userAgentArrayPosition == -1) {  // We are using a custom `userAgentString`.
434             // Set `userAgentSpinner` to `Custom`.
435             userAgentSpinner.setSelection(userAgentEntryValuesArrayAdapter.getPosition("Custom user agent"));
436
437             // Hide `userAgentTextView`.
438             userAgentTextView.setVisibility(View.GONE);
439
440             // Show `customUserAgentEditText` and set `userAgentString` as the text.
441             customUserAgentEditText.setVisibility(View.VISIBLE);
442             customUserAgentEditText.setText(currentUserAgentString);
443         } else{  // We are using one of the preset user agents.
444             // Set the `userAgentSpinner` selection.
445             userAgentSpinner.setSelection(userAgentArrayPosition);
446
447             // Show `userAgentTextView`.
448             userAgentTextView.setVisibility(View.VISIBLE);
449
450             // Hide `customUserAgentEditText`.
451             customUserAgentEditText.setVisibility(View.GONE);
452
453             // Set the user agent text.
454             switch (currentUserAgentString) {
455                 case "System default user agent":
456                     // Display the user agent text string.
457                     switch (defaultUserAgentString) {
458                         case "WebView default user agent":
459                             // Display the `WebView` default user agent.
460                             userAgentTextView.setText(webViewDefaultUserAgentString);
461                             break;
462
463                         case "Custom user agent":
464                             // Display the custom user agent.
465                             userAgentTextView.setText(defaultCustomUserAgentString);
466                             break;
467
468                         default:
469                             // Display the text from `defaultUserAgentString`.
470                             userAgentTextView.setText(defaultUserAgentString);
471                     }
472                     break;
473
474                 case "WebView default user agent":
475                     // Display the `WebView` default user agent.
476                     userAgentTextView.setText(webViewDefaultUserAgentString);
477                     break;
478
479                 default:
480                     // Display the text from `currentUserAgentString`.
481                     userAgentTextView.setText(currentUserAgentString);
482             }
483         }
484
485         // Set the selected font size.
486         int fontSizeArrayPosition = fontSizeEntryValuesArrayAdapter.getPosition(String.valueOf(fontSizeInt));
487         fontSizeSpinner.setSelection(fontSizeArrayPosition);
488
489         // Set the default font size text.
490         int defaultFontSizeArrayPosition = fontSizeEntryValuesArrayAdapter.getPosition(defaultFontSizeString);
491         fontSizeTextView.setText(fontSizeArrayAdapter.getItem(defaultFontSizeArrayPosition));
492
493         // Set the display options for `fontSizeTextView`.
494         if (fontSizeArrayPosition == 0) {  // System default font size is selected.  Display `fontSizeTextView`.
495             fontSizeTextView.setVisibility(View.VISIBLE);
496         } else {  // A custom font size is specified.  Hide `fontSizeTextView`.
497             fontSizeTextView.setVisibility(View.GONE);
498         }
499
500         // Set the selected display website images mode.
501         displayWebpageImagesSpinner.setSelection(displayImagesInt);
502
503         // Set the default display images text.
504         if (defaultDisplayWebpageImagesBoolean) {
505             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(1));
506         } else {
507             displayImagesTextView.setText(displayImagesArrayAdapter.getItem(2));
508         }
509
510         // Set the display website images icon and `TextView` settings.
511         switch (displayImagesInt) {
512             case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_SYSTEM_DEFAULT:
513                 if (MainWebViewActivity.displayWebpageImagesBoolean) {
514                     // Set the icon according to the theme.
515                     if (MainWebViewActivity.darkTheme) {
516                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
517                     } else {
518                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
519                     }
520                 } else {
521                     // Set the icon according to the theme.
522                     if (MainWebViewActivity.darkTheme) {
523                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
524                     } else {
525                         displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
526                     }
527                 }
528
529                 // Show `displayImagesTextView`.
530                 displayImagesTextView.setVisibility(View.VISIBLE);
531                 break;
532
533             case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED:
534                 // Set the icon according to the theme.
535                 if (MainWebViewActivity.darkTheme) {
536                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
537                 } else {
538                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
539                 }
540
541                 // Hide `displayImagesTextView`.
542                 displayImagesTextView.setVisibility(View.GONE);
543                 break;
544
545             case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED:
546                 // Set the icon according to the theme.
547                 if (MainWebViewActivity.darkTheme) {
548                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
549                 } else {
550                     displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
551                 }
552
553                 // Hide `displayImagesTextView`.
554                 displayImagesTextView.setVisibility(View.GONE);
555                 break;
556         }
557         
558         // Set the pinned SSL certificate icon.
559         if (pinnedSslCertificateInt == 1) {  // Pinned SSL certificate is enabled.
560             // Check the switch.
561             pinnedSslCertificateSwitch.setChecked(true);
562
563             // Set the icon according to the theme.
564             if (MainWebViewActivity.darkTheme) {
565                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark));
566             } else {
567                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_light));
568             }
569         } else {  // Pinned SSL certificate is disabled.
570             // Uncheck the switch.
571             pinnedSslCertificateSwitch.setChecked(false);
572
573             // Set the icon according to the theme.
574             if (MainWebViewActivity.darkTheme) {
575                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark));
576             } else {
577                 pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_light));
578             }
579         }
580
581         // Store the current date.
582         Date currentDate = Calendar.getInstance().getTime();
583
584         // Setup the `StringBuilders` to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
585         savedSslCertificateIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslCertificateIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
586         savedSslCertificateIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslCertificateIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
587         savedSslCertificateIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslCertificateIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
588         savedSslCertificateIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslCertificateIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
589         savedSslCertificateIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslCertificateIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
590
591         // Check the certificate `Common Name` against the domain name.
592         boolean savedSSlCertificateCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslCertificateIssuedToCNameString);
593
594         // Format the `issuedToCommonName` color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
595         if (savedSSlCertificateCommonNameMatchesDomainName) {
596             savedSslCertificateIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslCertificateIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
597         } else {
598             savedSslCertificateIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), savedSslCertificateIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
599         }
600
601         //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
602         if ((savedSslCertificateStartDate != null) && savedSslCertificateStartDate.after(currentDate)) {  // The certificate start date is in the future.
603             savedSslCertificateStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), savedSslCertificateStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
604         } else {  // The certificate start date is in the past.
605             savedSslCertificateStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), savedSslCertificateStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
606         }
607
608         // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
609         if ((savedSslCertificateEndDate != null) && savedSslCertificateEndDate.before(currentDate)) {  // The certificate end date is in the past.
610             savedSslCertificateEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), savedSslCertificateEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
611         } else {  // The certificate end date is in the future.
612             savedSslCertificateEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), savedSslCertificateEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
613         }
614
615         // Display the current website SSL certificate strings.
616         savedSslCertificateIssuedToCNameTextView.setText(savedSslCertificateIssuedToCNameStringBuilder);
617         savedSslCertificateIssuedToONameTextView.setText(savedSslCertificateIssuedToONameStringBuilder);
618         savedSslCertificateIssuedToUNameTextView.setText(savedSslCertificateIssuedToUNameStringBuilder);
619         savedSslCertificateIssuedByCNameTextView.setText(savedSslCertificateIssuedByCNameStringBuilder);
620         savedSslCertificateIssuedByONameTextView.setText(savedSslCertificateIssuedByONameStringBuilder);
621         savedSslCertificateIssuedByUNameTextView.setText(savedSslCertificateIssuedByUNameStringBuilder);
622         savedSslCertificateStartDateTextView.setText(savedSslCertificateStartDateStringBuilder);
623         savedSslCertificateEndDateTextView.setText(savedSslCertificateEndDateStringBuilder);
624
625         // Populate the current website SSL certificate if there is one.
626         if (currentWebsiteSslCertificate != null) {
627             // Get the strings from the SSL certificate.
628             String currentWebsiteCertificateIssuedToCNameString = currentWebsiteSslCertificate.getIssuedTo().getCName();
629             String currentWebsiteCertificateIssuedToONameString = currentWebsiteSslCertificate.getIssuedTo().getOName();
630             String currentWebsiteCertificateIssuedToUNameString = currentWebsiteSslCertificate.getIssuedTo().getUName();
631             String currentWebsiteCertificateIssuedByCNameString = currentWebsiteSslCertificate.getIssuedBy().getCName();
632             String currentWebsiteCertificateIssuedByONameString = currentWebsiteSslCertificate.getIssuedBy().getOName();
633             String currentWebsiteCertificateIssuedByUNameString = currentWebsiteSslCertificate.getIssuedBy().getUName();
634             Date currentWebsiteCertificateStartDate = currentWebsiteSslCertificate.getValidNotBeforeDate();
635             Date currentWebsiteCertificateEndDate = currentWebsiteSslCertificate.getValidNotAfterDate();
636
637             // Create a `SpannableStringBuilder` for each `TextView` that needs multiple colors of text.
638             SpannableStringBuilder currentWebsiteCertificateIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentWebsiteCertificateIssuedToCNameString);
639             SpannableStringBuilder currentWebsiteCertificateIssuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentWebsiteCertificateIssuedToONameString);
640             SpannableStringBuilder currentWebsiteCertificateIssuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentWebsiteCertificateIssuedToUNameString);
641             SpannableStringBuilder currentWebsiteCertificateIssuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentWebsiteCertificateIssuedByCNameString);
642             SpannableStringBuilder currentWebsiteCertificateIssuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentWebsiteCertificateIssuedByONameString);
643             SpannableStringBuilder currentWebsiteCertificateIssuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentWebsiteCertificateIssuedByUNameString);
644             SpannableStringBuilder currentWebsiteCertificateStartDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentWebsiteCertificateStartDate));
645             SpannableStringBuilder currentWebsiteCertificateEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentWebsiteCertificateEndDate));
646
647             // Setup the `StringBuilders` to display the general certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
648             currentWebsiteCertificateIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentWebsiteCertificateIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
649             currentWebsiteCertificateIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentWebsiteCertificateIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
650             currentWebsiteCertificateIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentWebsiteCertificateIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
651             currentWebsiteCertificateIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentWebsiteCertificateIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
652             currentWebsiteCertificateIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentWebsiteCertificateIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
653
654             // Check the certificate `Common Name` against the domain name.
655             boolean currentWebsiteCertificateCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, currentWebsiteCertificateIssuedToCNameString);
656
657             // Format the `issuedToCommonName` color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
658             if (currentWebsiteCertificateCommonNameMatchesDomainName) {
659                 currentWebsiteCertificateIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentWebsiteCertificateIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
660             } else {
661                 currentWebsiteCertificateIssuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), currentWebsiteCertificateIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
662             }
663
664             //  Format the start date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
665             if (currentWebsiteCertificateStartDate.after(currentDate)) {  // The certificate start date is in the future.
666                 currentWebsiteCertificateStartDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), currentWebsiteCertificateStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
667             } else {  // The certificate start date is in the past.
668                 currentWebsiteCertificateStartDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), currentWebsiteCertificateStartDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
669             }
670
671             // Format the end date color.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
672             if (currentWebsiteCertificateEndDate.before(currentDate)) {  // The certificate end date is in the past.
673                 currentWebsiteCertificateEndDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), currentWebsiteCertificateEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
674             } else {  // The certificate end date is in the future.
675                 currentWebsiteCertificateEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), currentWebsiteCertificateEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
676             }
677
678             // Display the current website SSL certificate strings.
679             currentWebsiteCertificateIssuedToCNameTextView.setText(currentWebsiteCertificateIssuedToCNameStringBuilder);
680             currentWebsiteCertificateIssuedToONameTextView.setText(currentWebsiteCertificateIssuedToONameStringBuilder);
681             currentWebsiteCertificateIssuedToUNameTextView.setText(currentWebsiteCertificateIssuedToUNameStringBuilder);
682             currentWebsiteCertificateIssuedByCNameTextView.setText(currentWebsiteCertificateIssuedByCNameStringBuilder);
683             currentWebsiteCertificateIssuedByONameTextView.setText(currentWebsiteCertificateIssuedByONameStringBuilder);
684             currentWebsiteCertificateIssuedByUNameTextView.setText(currentWebsiteCertificateIssuedByUNameStringBuilder);
685             currentWebsiteCertificateStartDateTextView.setText(currentWebsiteCertificateStartDateStringBuilder);
686             currentWebsiteCertificateEndDateTextView.setText(currentWebsiteCertificateEndDateStringBuilder);
687         }
688
689         // Set the initial display status for the SSL certificates.
690         if (pinnedSslCertificateSwitch.isChecked()) {
691             // Set the visibility of the saved SSL certificate.
692             if (savedSslCertificateIssuedToCNameString == null) {
693                 savedSslCertificateLinearLayout.setVisibility(View.GONE);
694             } else {
695                 savedSslCertificateLinearLayout.setVisibility(View.VISIBLE);
696             }
697
698             // Set the visibility of the current website SSL certificate.
699             if (currentWebsiteSslCertificate == null) {
700                 // Hide the SSL certificate.
701                 currentWebsiteCertificateLinearLayout.setVisibility(View.GONE);
702
703                 // Show the instruction.
704                 noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
705             } else {
706                 // Show the SSL certificate.
707                 currentWebsiteCertificateLinearLayout.setVisibility(View.VISIBLE);
708
709                 // Hide the instruction.
710                 noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
711             }
712
713             // Set the status of the radio buttons.
714             if (savedSslCertificateLinearLayout.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
715                 savedSslCertificateRadioButton.setChecked(true);
716                 currentWebsiteCertificateRadioButton.setChecked(false);
717             } else if (currentWebsiteCertificateLinearLayout.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
718                 currentWebsiteCertificateRadioButton.setChecked(true);
719                 savedSslCertificateRadioButton.setChecked(false);
720             } else {  // Neither SSL certificate is visible.
721                 savedSslCertificateRadioButton.setChecked(false);
722                 currentWebsiteCertificateRadioButton.setChecked(false);
723             }
724         } else {  // `pinnedSslCertificateSwitch` is not checked.
725             // Hide the SSl certificates and instructions.
726             savedSslCertificateLinearLayout.setVisibility(View.GONE);
727             currentWebsiteCertificateLinearLayout.setVisibility(View.GONE);
728             noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
729
730             // Uncheck the radio buttons.
731             savedSslCertificateRadioButton.setChecked(false);
732             currentWebsiteCertificateRadioButton.setChecked(false);
733         }
734
735
736         // Set the `javaScriptEnabledSwitch` `OnCheckedChangeListener()`.
737         javaScriptEnabledSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
738             @Override
739             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
740                 if (isChecked) {  // JavaScript is enabled.
741                     // Update the JavaScript icon.
742                     javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled));
743
744                     // Enable the DOM storage `Switch`.
745                     domStorageEnabledSwitch.setEnabled(true);
746
747                     // Update the DOM storage icon.
748                     if (domStorageEnabledSwitch.isChecked()) {  // DOM storage is enabled.
749                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
750                     } else {  // DOM storage is disabled.
751                         // Set the icon according to the theme.
752                         if (MainWebViewActivity.darkTheme) {
753                             domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
754                         } else {
755                             domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
756                         }
757                     }
758                 } else {  // JavaScript is disabled.
759                     // Update the JavaScript icon.
760                     javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode));
761
762                     // Disable the DOM storage `Switch`.
763                     domStorageEnabledSwitch.setEnabled(false);
764
765                     // Set the DOM storage icon according to the theme.
766                     if (MainWebViewActivity.darkTheme) {
767                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_dark));
768                     } else {
769                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_light));
770                     }
771                 }
772             }
773         });
774
775         // Set the `firstPartyCookiesEnabledSwitch` `OnCheckedChangeListener()`.
776         firstPartyCookiesEnabledSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
777             @Override
778             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
779                 if (isChecked) {  // First-party cookies are enabled.
780                     // Update the first-party cookies icon.
781                     firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_enabled));
782
783                     // Enable the third-party cookies `Switch`.
784                     thirdPartyCookiesEnabledSwitch.setEnabled(true);
785
786                     // Update the third-party cookies icon.
787                     if (thirdPartyCookiesEnabledSwitch.isChecked()) {  // Third-party cookies are enabled.
788                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
789                     } else {  // Third-party cookies are disabled.
790                         // Set the third-party cookies icon according to the theme.
791                         if (MainWebViewActivity.darkTheme) {
792                             thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
793                         } else {
794                             thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
795                         }
796                     }
797                 } else {  // First-party cookies are disabled.
798                     // Update the first-party cookies icon according to the theme.
799                     if (MainWebViewActivity.darkTheme) {
800                         firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
801                     } else {
802                         firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
803                     }
804
805                     // Disable the third-party cookies `Switch`.
806                     thirdPartyCookiesEnabledSwitch.setEnabled(false);
807
808                     // Set the third-party cookies icon according to the theme.
809                     if (MainWebViewActivity.darkTheme) {
810                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_dark));
811                     } else {
812                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_light));
813                     }
814                 }
815             }
816         });
817
818         // Set the `thirdPartyCookiesEnabledSwitch` `OnCheckedChangeListener()`.
819         thirdPartyCookiesEnabledSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
820             @Override
821             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
822                 // Update the icon.
823                 if (isChecked) {
824                     thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
825                 } else {
826                     // Update the third-party cookies icon according to the theme.
827                     if (MainWebViewActivity.darkTheme) {
828                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_dark));
829                     } else {
830                         thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_light));
831                     }
832                 }
833             }
834         });
835
836         // Set the `domStorageEnabledSwitch` `OnCheckedChangeListener()`.
837         domStorageEnabledSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
838             @Override
839             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
840                 // Update the icon.
841                 if (isChecked) {
842                     domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
843                 } else {
844                     // Set the icon according to the theme.
845                     if (MainWebViewActivity.darkTheme) {
846                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark));
847                     } else {
848                         domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light));
849                     }
850                 }
851             }
852         });
853
854         // Set the `formDataEnabledSwitch` `OnCheckedChangeListener()`.
855         formDataEnabledSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
856             @Override
857             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
858                 // Update the icon.
859                 if (isChecked) {
860                     formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_enabled));
861                 } else {
862                     // Set the icon according to the theme.
863                     if (MainWebViewActivity.darkTheme) {
864                         formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_dark));
865                     } else {
866                         formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_light));
867                     }
868                 }
869             }
870         });
871
872         // Set the `userAgentSpinner` `onItemClickListener()`.
873         userAgentSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
874             @Override
875             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
876                 // Store the new user agent string.
877                 String newUserAgentString = resources.getStringArray(R.array.domain_settings_user_agent_entry_values)[position];
878
879                 // Set the new user agent.
880                 switch (newUserAgentString) {
881                     case "System default user agent":
882                         // Show `userAgentTextView`.
883                         userAgentTextView.setVisibility(View.VISIBLE);
884
885                         // Hide `customUserAgentEditText`.
886                         customUserAgentEditText.setVisibility(View.GONE);
887
888                         // Set the user text.
889                         switch (defaultUserAgentString) {
890                             case "WebView default user agent":
891                                 // Display the `WebView` default user agent.
892                                 userAgentTextView.setText(webViewDefaultUserAgentString);
893                                 break;
894
895                             case "Custom user agent":
896                                 // Display the custom user agent.
897                                 userAgentTextView.setText(defaultCustomUserAgentString);
898                                 break;
899
900                             default:
901                                 // Display the text from `defaultUserAgentString`.
902                                 userAgentTextView.setText(defaultUserAgentString);
903                         }
904                         break;
905
906                     case "WebView default user agent":
907                         // Show `userAgentTextView` and set the text.
908                         userAgentTextView.setVisibility(View.VISIBLE);
909                         userAgentTextView.setText(webViewDefaultUserAgentString);
910
911                         // Hide `customUserAgentEditText`.
912                         customUserAgentEditText.setVisibility(View.GONE);
913                         break;
914
915                     case "Custom user agent":
916                         // Hide `userAgentTextView`.
917                         userAgentTextView.setVisibility(View.GONE);
918
919                         // Show `customUserAgentEditText` and set `userAgentString` as the text.
920                         customUserAgentEditText.setVisibility(View.VISIBLE);
921                         customUserAgentEditText.setText(currentUserAgentString);
922                         break;
923
924                     default:
925                         // Show `userAgentTextView` and set the text.
926                         userAgentTextView.setVisibility(View.VISIBLE);
927                         userAgentTextView.setText(newUserAgentString);
928
929                         // Hide `customUserAgentEditText`.
930                         customUserAgentEditText.setVisibility(View.GONE);
931                 }
932             }
933
934             @Override
935             public void onNothingSelected(AdapterView<?> parent) {
936                 // Do nothing.
937             }
938         });
939
940         // Set the `fontSizeSpinner` `onItemSelectedListener()`.
941         fontSizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
942             @Override
943             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
944                 // Update the display options for `fontSizeTextView`.
945                 if (position == 0) {  // System default font size has been selected.  Display `fontSizeTextView`.
946                     fontSizeTextView.setVisibility(View.VISIBLE);
947                 } else {  // A custom font size has been selected.  Hide `fontSizeTextView`.
948                     fontSizeTextView.setVisibility(View.GONE);
949                 }
950             }
951
952             @Override
953             public void onNothingSelected(AdapterView<?> parent) {
954                 // Do nothing.
955             }
956         });
957
958         // Set the `displayWebpageImagesSpinner` `onItemSelectedListener()`.
959         displayWebpageImagesSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
960             @Override
961             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
962                 // Update the icon and the visibility of `displayImagesTextView`.
963                 switch (position) {
964                     case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_SYSTEM_DEFAULT:
965                         if (MainWebViewActivity.displayWebpageImagesBoolean) {
966                             // Set the icon according to the theme.
967                             if (MainWebViewActivity.darkTheme) {
968                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
969                             } else {
970                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
971                             }
972                         } else {
973                             // Set the icon according to the theme.
974                             if (MainWebViewActivity.darkTheme) {
975                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
976                             } else {
977                                 displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
978                             }
979                         }
980
981                         // Show `displayImagesTextView`.
982                         displayImagesTextView.setVisibility(View.VISIBLE);
983                         break;
984
985                     case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED:
986                         // Set the icon according to the theme.
987                         if (MainWebViewActivity.darkTheme) {
988                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark));
989                         } else {
990                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light));
991                         }
992
993                         // Hide `displayImagesTextView`.
994                         displayImagesTextView.setVisibility(View.GONE);
995                         break;
996
997                     case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED:
998                         // Set the icon according to the theme.
999                         if (MainWebViewActivity.darkTheme) {
1000                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark));
1001                         } else {
1002                             displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_light));
1003                         }
1004
1005                         // Hide `displayImagesTextView`.
1006                         displayImagesTextView.setVisibility(View.GONE);
1007                         break;
1008                 }
1009             }
1010
1011             @Override
1012             public void onNothingSelected(AdapterView<?> parent) {
1013                 // Do nothing.
1014             }
1015         });
1016         
1017         // Set the `pinnedSSLCertificateSwitch` `onCheckedChangeListener()`.
1018         pinnedSslCertificateSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
1019             @Override
1020             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
1021                 // Update the icon
1022                 if (isChecked) {  // Pinned SSL certificate is enabled.
1023                     // Set the icon according to the theme.
1024                     if (MainWebViewActivity.darkTheme) {
1025                         pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark));
1026                     } else {
1027                         pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_light));
1028                     }
1029
1030                     // Update the visibility of the saved SSL certificate.
1031                     if (savedSslCertificateIssuedToCNameString == null) {
1032                         savedSslCertificateLinearLayout.setVisibility(View.GONE);
1033                     } else {
1034                         savedSslCertificateLinearLayout.setVisibility(View.VISIBLE);
1035                     }
1036
1037                     // Update the visibility of the current website SSL certificate.
1038                     if (currentWebsiteSslCertificate == null) {
1039                         // Hide the SSL certificate.
1040                         currentWebsiteCertificateLinearLayout.setVisibility(View.GONE);
1041
1042                         // Show the instruction.
1043                         noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE);
1044                     } else {
1045                         // Show the SSL certificate.
1046                         currentWebsiteCertificateLinearLayout.setVisibility(View.VISIBLE);
1047
1048                         // Hide the instruction.
1049                         noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1050                     }
1051
1052                     // Set the status of the radio buttons.
1053                     if (savedSslCertificateLinearLayout.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is displayed.
1054                         savedSslCertificateRadioButton.setChecked(true);
1055                         currentWebsiteCertificateRadioButton.setChecked(false);
1056                     } else if (currentWebsiteCertificateLinearLayout.getVisibility() == View.VISIBLE) {  // The saved SSL certificate is hidden but the current website SSL certificate is visible.
1057                         currentWebsiteCertificateRadioButton.setChecked(true);
1058                         savedSslCertificateRadioButton.setChecked(false);
1059                     } else {  // Neither SSL certificate is visible.
1060                         savedSslCertificateRadioButton.setChecked(false);
1061                         currentWebsiteCertificateRadioButton.setChecked(false);
1062                     }
1063                 } else {  // Pinned SSL certificate is disabled.
1064                     // Set the icon according to the theme.
1065                     if (MainWebViewActivity.darkTheme) {
1066                         pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark));
1067                     } else {
1068                         pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_light));
1069                     }
1070
1071                     // Hide the SSl certificates and instructions.
1072                     savedSslCertificateLinearLayout.setVisibility(View.GONE);
1073                     currentWebsiteCertificateLinearLayout.setVisibility(View.GONE);
1074                     noCurrentWebsiteCertificateTextView.setVisibility(View.GONE);
1075
1076                     // Uncheck the radio buttons.
1077                     savedSslCertificateRadioButton.setChecked(false);
1078                     currentWebsiteCertificateRadioButton.setChecked(false);
1079                 }
1080             }
1081         });
1082
1083         savedSslCertificateLinearLayout.setOnClickListener(new View.OnClickListener() {
1084             @Override
1085             public void onClick(View v) {
1086                 savedSslCertificateRadioButton.setChecked(true);
1087                 currentWebsiteCertificateRadioButton.setChecked(false);
1088             }
1089         });
1090
1091         savedSslCertificateRadioButton.setOnClickListener(new View.OnClickListener() {
1092             @Override
1093             public void onClick(View v) {
1094                 savedSslCertificateRadioButton.setChecked(true);
1095                 currentWebsiteCertificateRadioButton.setChecked(false);
1096             }
1097         });
1098
1099         currentWebsiteCertificateLinearLayout.setOnClickListener(new View.OnClickListener() {
1100             @Override
1101             public void onClick(View v) {
1102                 currentWebsiteCertificateRadioButton.setChecked(true);
1103                 savedSslCertificateRadioButton.setChecked(false);
1104             }
1105         });
1106
1107         currentWebsiteCertificateRadioButton.setOnClickListener(new View.OnClickListener() {
1108             @Override
1109             public void onClick(View v) {
1110                 currentWebsiteCertificateRadioButton.setChecked(true);
1111                 savedSslCertificateRadioButton.setChecked(false);
1112             }
1113         });
1114
1115         return domainSettingsView;
1116     }
1117
1118     private boolean checkDomainNameAgainstCertificate(String domainName, String certificateCommonName) {
1119         // Initialize `domainNamesMatch`.
1120         boolean domainNamesMatch = false;
1121
1122         // Check if the domains match.
1123         if (domainName.equals(certificateCommonName)) {
1124             domainNamesMatch = true;
1125         }
1126
1127         // Check various wildcard permutations if `domainName` and `certificateCommonName` are not empty.  `noinspection ConstantCondition` removes Android Studio's incorrect lint warning that `domainName` can never be `null`.
1128         //noinspection ConstantConditions
1129         if ((domainName != null) && (certificateCommonName != null)) {
1130             // If `domainName` starts with a wildcard, check the base domain against all the subdomains of `certificateCommonName`.
1131             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2)) {
1132                 // Remove the initial `*.`.
1133                 String baseDomainName = domainName.substring(2);
1134
1135                 // Setup a copy of `certificateCommonName` to test subdomains.
1136                 String certificateCommonNameSubdomain = certificateCommonName;
1137
1138                 // Check all the subdomains in `certificateCommonNameSubdomains` against `baseDomainName`.
1139                 while (!domainNamesMatch && certificateCommonNameSubdomain.contains(".")) {  // Stop checking if we know that `domainNamesMatch` is `true` or if we run out of  `.`.
1140                     // Test the `certificateCommonNameSubdomain` against `baseDomainName`.
1141                     if (certificateCommonNameSubdomain.equals(baseDomainName)) {
1142                         domainNamesMatch = true;
1143                     }
1144
1145                     // Strip out the lowest subdomain of `certificateCommonNameSubdomain`.
1146                     try {
1147                         certificateCommonNameSubdomain = certificateCommonNameSubdomain.substring(certificateCommonNameSubdomain.indexOf(".") + 1);
1148                     } catch (IndexOutOfBoundsException e) {  // `certificateCommonNameSubdomain` ends with `.`.
1149                         certificateCommonNameSubdomain = "";
1150                     }
1151                 }
1152             }
1153
1154             // If `certificateCommonName` starts with a wildcard, check the base common name against all the subdomains of `domainName`.
1155             if (!domainNamesMatch && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
1156                 // Remove the initial `*.`.
1157                 String baseCertificateCommonName = certificateCommonName.substring(2);
1158
1159                 // Setup a copy of `domainName` to test subdomains.
1160                 String domainNameSubdomain = domainName;
1161
1162                 // Check all the subdomains in `domainNameSubdomain` against `baseCertificateCommonName`.
1163                 while (!domainNamesMatch && domainNameSubdomain.contains(".") && (domainNameSubdomain.length() > 2)) {
1164                     // Test the `domainNameSubdomain` against `baseCertificateCommonName`.
1165                     if (domainNameSubdomain.equals(baseCertificateCommonName)) {
1166                         domainNamesMatch = true;
1167                     }
1168
1169                     // Strip out the lowest subdomain of `domainNameSubdomain`.
1170                     try {
1171                         domainNameSubdomain = domainNameSubdomain.substring(domainNameSubdomain.indexOf(".") + 1);
1172                     } catch (IndexOutOfBoundsException e) { // `domainNameSubdomain` ends with `.`.
1173                         domainNameSubdomain = "";
1174                     }
1175                 }
1176             }
1177
1178             // If both names start with a wildcard, check if the root of one contains the root of the other.
1179             if (!domainNamesMatch && domainName.startsWith("*.") && (domainName.length() > 2) && certificateCommonName.startsWith("*.") && (certificateCommonName.length() > 2)) {
1180                 // Remove the wildcards.
1181                 String rootDomainName = domainName.substring(2);
1182                 String rootCertificateCommonName = certificateCommonName.substring(2);
1183
1184                 // Check if one name ends with the contents of the other.  If so, there will be overlap in the their wildcard subdomains.
1185                 if (rootDomainName.endsWith(rootCertificateCommonName) || rootCertificateCommonName.endsWith(rootDomainName)) {
1186                     domainNamesMatch = true;
1187                 }
1188             }
1189         }
1190
1191         return domainNamesMatch;
1192     }
1193 }