72fc85aeeee2752e1968bbf638166449f9b81e27
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / dialogs / PinnedMismatchDialog.java
1 /*
2  * Copyright © 2017-2019 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.dialogs;
21
22 import android.annotation.SuppressLint;
23 import android.app.AlertDialog;
24 import android.app.Dialog;
25 import android.content.Context;
26 import android.content.DialogInterface;
27 import android.content.SharedPreferences;
28 import android.graphics.Bitmap;
29 import android.graphics.drawable.BitmapDrawable;
30 import android.graphics.drawable.Drawable;
31 import android.net.Uri;
32 import android.net.http.SslCertificate;
33 import android.os.Bundle;
34 import android.preference.PreferenceManager;
35 import android.text.SpannableStringBuilder;
36 import android.text.Spanned;
37 import android.text.style.ForegroundColorSpan;
38 import android.view.View;
39 import android.view.ViewGroup;
40 import android.view.WindowManager;
41 import android.widget.TextView;
42
43 import com.google.android.material.tabs.TabLayout;
44
45 import com.stoutner.privacybrowser.R;
46 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
47 import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
48 import com.stoutner.privacybrowser.views.NestedScrollWebView;
49 import com.stoutner.privacybrowser.views.WrapVerticalContentViewPager;
50 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
51
52 import java.text.DateFormat;
53 import java.util.ArrayList;
54 import java.util.Date;
55
56 import androidx.annotation.NonNull;
57 import androidx.core.content.ContextCompat;
58 import androidx.fragment.app.DialogFragment;  // The AndroidX dialog fragment must be used or an error is produced on API <=22.
59 import androidx.viewpager.widget.PagerAdapter;
60
61 public class PinnedMismatchDialog extends DialogFragment {
62     // Declare the class variables.
63     private NestedScrollWebView nestedScrollWebView;
64     private String currentSslIssuedToCName;
65     private String currentSslIssuedToOName;
66     private String currentSslIssuedToUName;
67     private String currentSslIssuedByCName;
68     private String currentSslIssuedByOName;
69     private String currentSslIssuedByUName;
70     private Date currentSslStartDate;
71     private Date currentSslEndDate;
72
73     public static PinnedMismatchDialog displayDialog(long webViewFragmentId) {
74         // Create an arguments bundle.
75         Bundle argumentsBundle = new Bundle();
76
77         // Store the WebView fragment ID in the bundle.
78         argumentsBundle.putLong("webview_fragment_id", webViewFragmentId);
79
80         // Create a new instance of the pinned mismatch dialog.
81         PinnedMismatchDialog pinnedMismatchDialog = new PinnedMismatchDialog();
82
83         // Add the arguments bundle to the new instance.
84         pinnedMismatchDialog.setArguments(argumentsBundle);
85
86         // Make it so.
87         return pinnedMismatchDialog;
88     }
89
90     // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
91     @SuppressLint("InflateParams")
92     @Override
93     @NonNull
94     public Dialog onCreateDialog(Bundle savedInstanceState) {
95         // Get the arguments.
96         Bundle arguments = getArguments();
97
98         // Remove the incorrect lint warning below that `.getArguments().getInt()` might be null.
99         assert arguments != null;
100
101         // Get the current position of this WebView fragment.
102         int webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(arguments.getLong("webview_fragment_id"));
103
104         // Get the WebView tab fragment.
105         WebViewTabFragment webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition);
106
107         // Get the fragment view.
108         View fragmentView = webViewTabFragment.getView();
109
110         // Remove the incorrect lint warning below that the fragment view might be null.
111         assert fragmentView != null;
112
113         // Get a handle for the current WebView.
114         nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
115
116         // Use an alert dialog builder to create the alert dialog.
117         AlertDialog.Builder dialogBuilder;
118
119         // Get a handle for the shared preferences.
120         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
121
122         // Get the screenshot and theme preferences.
123         boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
124         boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
125
126         // Set the style according to the theme.
127         if (darkTheme) {
128             // Set the dialog theme.
129             dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogDark);
130         } else {
131             // Set the dialog theme.
132             dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogLight);
133         }
134
135         // Get the context.
136         Context context = getContext();
137
138         // Remove the incorrect lint warning below that the context might be null.
139         assert context != null;
140
141         // Get the favorite icon.
142         Bitmap favoriteIconBitmap = nestedScrollWebView.getFavoriteOrDefaultIcon();
143
144         // Get the default favorite icon drawable.  `ContextCompat` must be used until API >= 21.
145         Drawable defaultFavoriteIconDrawable = ContextCompat.getDrawable(context, R.drawable.world);
146
147         // Cast the favorite icon drawable to a bitmap drawable.
148         BitmapDrawable defaultFavoriteIconBitmapDrawable = (BitmapDrawable) defaultFavoriteIconDrawable;
149
150         // Remove the incorrect warning below that the favorite icon bitmap drawable might be null.
151         assert defaultFavoriteIconBitmapDrawable != null;
152
153         // Store the default icon bitmap.
154         Bitmap defaultFavoriteIconBitmap = defaultFavoriteIconBitmapDrawable.getBitmap();
155
156         // Set the favorite icon as the dialog icon if it exists.
157         if (favoriteIconBitmap.sameAs(defaultFavoriteIconBitmap)) {  // There is no website favorite icon.
158             // Set the icon according to the theme.
159             if (darkTheme) {
160                 dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_dark);
161             } else {
162                 dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_light);
163             }
164         } else {  // There is a favorite icon.
165             // Create a drawable version of the favorite icon.
166             Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), favoriteIconBitmap);
167
168             // Set the icon.
169             dialogBuilder.setIcon(favoriteIconDrawable);
170         }
171
172         // Setup the neutral button.
173         dialogBuilder.setNeutralButton(R.string.update, (DialogInterface dialog, int which) -> {
174             // Initialize the long date variables.  If the date is null, a long value of `0` will be stored in the Domains database entry.
175             long currentSslStartDateLong = 0;
176             long currentSslEndDateLong = 0;
177
178             // Convert the `Dates` into `longs`.
179             if (currentSslStartDate != null) {
180                 currentSslStartDateLong = currentSslStartDate.getTime();
181             }
182
183             if (currentSslEndDate != null) {
184                 currentSslEndDateLong = currentSslEndDate.getTime();
185             }
186
187             // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
188             DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0);
189
190             // Update the SSL certificate if it is pinned.
191             if (nestedScrollWebView.hasPinnedSslCertificate()) {
192                 // Update the pinned SSL certificate in the domain database.
193                 domainsDatabaseHelper.updatePinnedSslCertificate(nestedScrollWebView.getDomainSettingsDatabaseId(), currentSslIssuedToCName, currentSslIssuedToOName, currentSslIssuedToUName,
194                         currentSslIssuedByCName, currentSslIssuedByOName, currentSslIssuedByUName, currentSslStartDateLong, currentSslEndDateLong);
195
196                 // Update the pinned SSL certificate in the nested scroll WebView.
197                 nestedScrollWebView.setPinnedSslCertificate(currentSslIssuedToCName, currentSslIssuedToOName, currentSslIssuedToUName, currentSslIssuedByCName, currentSslIssuedByOName, currentSslIssuedByUName,
198                         currentSslStartDate, currentSslEndDate);
199             }
200
201             // Update the IP addresses if they are pinned.
202             if (nestedScrollWebView.hasPinnedIpAddresses()) {
203                 // Update the pinned IP addresses in the domain database.
204                 domainsDatabaseHelper.updatePinnedIpAddresses(nestedScrollWebView.getDomainSettingsDatabaseId(), nestedScrollWebView.getCurrentIpAddresses());
205
206                 // Update the pinned IP addresses in the nested scroll WebView.
207                 nestedScrollWebView.setPinnedIpAddresses(nestedScrollWebView.getCurrentIpAddresses());
208             }
209         });
210
211         // Setup the back button.
212         dialogBuilder.setNegativeButton(R.string.back, (DialogInterface dialog, int which) -> {
213             if (nestedScrollWebView.canGoBack()) {  // There is a back page in the history.
214                 // Reset the current domain name so that navigation works if third-party requests are blocked.
215                 nestedScrollWebView.resetCurrentDomainName();
216
217                 // Set navigating history so that the domain settings are applied when the new URL is loaded.
218                 nestedScrollWebView.setNavigatingHistory(true);
219
220                 // Go back.
221                 nestedScrollWebView.goBack();
222             } else {  // There are no pages to go back to.
223                 // Load a blank page
224                 nestedScrollWebView.loadUrl("");
225             }
226         });
227
228         // Setup the proceed button.
229         dialogBuilder.setPositiveButton(R.string.proceed, (DialogInterface dialog, int which) -> {
230             // Do not check the pinned information for this domain again until the domain changes.
231             nestedScrollWebView.setIgnorePinnedDomainInformation(true);
232         });
233
234         // Set the title.
235         dialogBuilder.setTitle(R.string.pinned_mismatch);
236
237         // Remove the incorrect lint warning below that `getLayoutInflater()` might be null.
238         assert getActivity() != null;
239
240         // Set the layout.  The parent view is `null` because it will be assigned by `AlertDialog`.
241         // For some reason, `getLayoutInflater()` without `getActivity()` produces an endless loop (probably a bug that will be fixed at some point in the future).
242         dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.pinned_mismatch_linearlayout, null));
243
244         // Create an alert dialog from the alert dialog builder.
245         final AlertDialog alertDialog = dialogBuilder.create();
246
247         // Disable screenshots if not allowed.
248         if (!allowScreenshots) {
249             // Remove the warning below that `getWindow()` might be null.
250             assert alertDialog.getWindow() != null;
251
252             // Disable screenshots.
253             alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
254         }
255
256         // Show the alert dialog so the items in the layout can be modified.
257         alertDialog.show();
258
259         //  Setup the view pager.
260         WrapVerticalContentViewPager wrapVerticalContentViewPager = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_viewpager);
261         wrapVerticalContentViewPager.setAdapter(new pagerAdapter());
262
263         // Setup the tab layout and connect it to the view pager.
264         TabLayout tabLayout = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_tablayout);
265         tabLayout.setupWithViewPager(wrapVerticalContentViewPager);
266
267         // `onCreateDialog()` requires the return of an `AlertDialog`.
268         return alertDialog;
269     }
270
271     private class pagerAdapter extends PagerAdapter {
272         @Override
273         public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
274             // Check to see if the `View` and the `Object` are the same.
275             return (view == object);
276         }
277
278         @Override
279         public int getCount() {
280             // There are two tabs.
281             return 2;
282         }
283
284         @Override
285         public CharSequence getPageTitle(int position) {
286             // Return the current tab title.
287             if (position == 0) {  // The current SSL certificate tab.
288                 return getString(R.string.current);
289             } else {  // The pinned SSL certificate tab.
290                 return getString(R.string.pinned);
291             }
292         }
293
294         @Override
295         @NonNull
296         public Object instantiateItem(@NonNull ViewGroup container, int position) {
297             // Inflate the scroll view for this tab.
298             ViewGroup tabViewGroup = (ViewGroup) getLayoutInflater().inflate(R.layout.pinned_mismatch_scrollview, container, false);
299
300             // Get handles for the `TextViews`.
301             TextView domainNameTextView = tabViewGroup.findViewById(R.id.domain_name);
302             TextView ipAddressesTextView = tabViewGroup.findViewById(R.id.ip_addresses);
303             TextView issuedToCNameTextView = tabViewGroup.findViewById(R.id.issued_to_cname);
304             TextView issuedToONameTextView = tabViewGroup.findViewById(R.id.issued_to_oname);
305             TextView issuedToUNameTextView = tabViewGroup.findViewById(R.id.issued_to_uname);
306             TextView issuedByCNameTextView = tabViewGroup.findViewById(R.id.issued_by_cname);
307             TextView issuedByONameTextView = tabViewGroup.findViewById(R.id.issued_by_oname);
308             TextView issuedByUNameTextView = tabViewGroup.findViewById(R.id.issued_by_uname);
309             TextView startDateTextView = tabViewGroup.findViewById(R.id.start_date);
310             TextView endDateTextView = tabViewGroup.findViewById(R.id.end_date);
311
312             // Setup the labels.
313             String domainNameLabel = getString(R.string.domain_label) + "  ";
314             String ipAddressesLabel = getString(R.string.ip_addresses) + "  ";
315             String cNameLabel = getString(R.string.common_name) + "  ";
316             String oNameLabel = getString(R.string.organization) + "  ";
317             String uNameLabel = getString(R.string.organizational_unit) + "  ";
318             String startDateLabel = getString(R.string.start_date) + "  ";
319             String endDateLabel = getString(R.string.end_date) + "  ";
320
321             // Convert the URL to a URI.
322             Uri currentUri = Uri.parse(nestedScrollWebView.getUrl());
323
324             // Get the current host from the URI.
325             String domainName = currentUri.getHost();
326
327             // Get the current website SSL certificate.
328             SslCertificate sslCertificate = nestedScrollWebView.getCertificate();
329
330             // Extract the individual pieces of information from the current website SSL certificate if it is not null.
331             if (sslCertificate != null) {
332                 currentSslIssuedToCName = sslCertificate.getIssuedTo().getCName();
333                 currentSslIssuedToOName = sslCertificate.getIssuedTo().getOName();
334                 currentSslIssuedToUName = sslCertificate.getIssuedTo().getUName();
335                 currentSslIssuedByCName = sslCertificate.getIssuedBy().getCName();
336                 currentSslIssuedByOName = sslCertificate.getIssuedBy().getOName();
337                 currentSslIssuedByUName = sslCertificate.getIssuedBy().getUName();
338                 currentSslStartDate = sslCertificate.getValidNotBeforeDate();
339                 currentSslEndDate = sslCertificate.getValidNotAfterDate();
340             } else {
341                 // Initialize the current website SSL certificate variables with blank information.
342                 currentSslIssuedToCName = "";
343                 currentSslIssuedToOName = "";
344                 currentSslIssuedToUName = "";
345                 currentSslIssuedByCName = "";
346                 currentSslIssuedByOName = "";
347                 currentSslIssuedByUName = "";
348             }
349
350             // Get the pinned SSL certificate.
351             ArrayList<Object> pinnedSslCertificateArrayList = nestedScrollWebView.getPinnedSslCertificate();
352
353             // Extract the arrays from the array list.
354             String[] pinnedSslCertificateStringArray = (String[]) pinnedSslCertificateArrayList.get(0);
355             Date[] pinnedSslCertificateDateArray = (Date[]) pinnedSslCertificateArrayList.get(1);
356
357             // Setup the domain name spannable string builder.
358             SpannableStringBuilder domainNameStringBuilder = new SpannableStringBuilder(domainNameLabel + domainName);
359
360             // Initialize the spannable string builders.
361             SpannableStringBuilder ipAddressesStringBuilder;
362             SpannableStringBuilder issuedToCNameStringBuilder;
363             SpannableStringBuilder issuedToONameStringBuilder;
364             SpannableStringBuilder issuedToUNameStringBuilder;
365             SpannableStringBuilder issuedByCNameStringBuilder;
366             SpannableStringBuilder issuedByONameStringBuilder;
367             SpannableStringBuilder issuedByUNameStringBuilder;
368             SpannableStringBuilder startDateStringBuilder;
369             SpannableStringBuilder endDateStringBuilder;
370
371             // Setup the spannable string builders for each tab.
372             if (position == 0) {  // Setup the current settings tab.
373                 // Create the string builders.
374                 ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + nestedScrollWebView.getCurrentIpAddresses());
375                 issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentSslIssuedToCName);
376                 issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentSslIssuedToOName);
377                 issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentSslIssuedToUName);
378                 issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentSslIssuedByCName);
379                 issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentSslIssuedByOName);
380                 issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentSslIssuedByUName);
381
382                 // Set the dates if they aren't `null`.
383                 if (currentSslStartDate == null) {
384                     startDateStringBuilder = new SpannableStringBuilder(startDateLabel);
385                 } else {
386                     startDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslStartDate));
387                 }
388
389                 if (currentSslEndDate == null) {
390                     endDateStringBuilder = new SpannableStringBuilder(endDateLabel);
391                 } else {
392                     endDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslEndDate));
393                 }
394             } else {  // Setup the pinned settings tab.
395                 // Create the string builders.
396                 ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + nestedScrollWebView.getPinnedIpAddresses());
397                 issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + pinnedSslCertificateStringArray[0]);
398                 issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + pinnedSslCertificateStringArray[1]);
399                 issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + pinnedSslCertificateStringArray[2]);
400                 issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + pinnedSslCertificateStringArray[3]);
401                 issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + pinnedSslCertificateStringArray[4]);
402                 issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + pinnedSslCertificateStringArray[5]);
403
404                 // Set the dates if they aren't `null`.
405                 if (pinnedSslCertificateDateArray[0] == null) {
406                     startDateStringBuilder = new SpannableStringBuilder(startDateLabel);
407                 } else {
408                     startDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[0]));
409                 }
410
411                 if (pinnedSslCertificateDateArray[1] == null) {
412                     endDateStringBuilder = new SpannableStringBuilder(endDateLabel);
413                 } else {
414                     endDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[1]));
415                 }
416             }
417
418             // Get a handle for the shared preferences.
419             SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
420
421             // Get the screenshot and theme preferences.
422             boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
423
424             // Create a red foreground color span.  The deprecated `getResources().getColor` must be used until the minimum API >= 23.
425             ForegroundColorSpan redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
426
427             // Create a blue foreground color span.
428             ForegroundColorSpan blueColorSpan;
429
430             // Set the blue color span according to the theme.  The deprecated `getResources().getColor` must be used until the minimum API >= 23.
431             if (darkTheme) {
432                 blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_400));
433             } else {
434                 blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
435             }
436
437             // Set the domain name to be blue.
438             domainNameStringBuilder.setSpan(blueColorSpan, domainNameLabel.length(), domainNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
439
440             // Color coordinate the IP addresses if they are pinned.
441             if (nestedScrollWebView.hasPinnedIpAddresses()) {
442                 if (nestedScrollWebView.getCurrentIpAddresses().equals(nestedScrollWebView.getPinnedIpAddresses())) {
443                     ipAddressesStringBuilder.setSpan(blueColorSpan, ipAddressesLabel.length(), ipAddressesStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
444                 } else {
445                     ipAddressesStringBuilder.setSpan(redColorSpan, ipAddressesLabel.length(), ipAddressesStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
446                 }
447             }
448
449             // Color coordinate the SSL certificate fields if they are pinned.
450             if (nestedScrollWebView.hasPinnedSslCertificate()) {
451                 if (currentSslIssuedToCName.equals(pinnedSslCertificateStringArray[0])) {
452                     issuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
453                 } else {
454                     issuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
455                 }
456
457                 if (currentSslIssuedToOName.equals(pinnedSslCertificateStringArray[1])) {
458                     issuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
459                 } else {
460                     issuedToONameStringBuilder.setSpan(redColorSpan, oNameLabel.length(), issuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
461                 }
462
463                 if (currentSslIssuedToUName.equals(pinnedSslCertificateStringArray[2])) {
464                     issuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
465                 } else {
466                     issuedToUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length(), issuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
467                 }
468
469                 if (currentSslIssuedByCName.equals(pinnedSslCertificateStringArray[3])) {
470                     issuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
471                 } else {
472                     issuedByCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
473                 }
474
475                 if (currentSslIssuedByOName.equals(pinnedSslCertificateStringArray[4])) {
476                     issuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
477                 } else {
478                     issuedByONameStringBuilder.setSpan(redColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
479                 }
480
481                 if (currentSslIssuedByUName.equals(pinnedSslCertificateStringArray[5])) {
482                     issuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
483                 } else {
484                     issuedByUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
485                 }
486
487                 if ((currentSslStartDate != null) && currentSslStartDate.equals(pinnedSslCertificateDateArray[0])) {
488                     startDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
489                 } else {
490                     startDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
491                 }
492
493                 if ((currentSslEndDate != null) && currentSslEndDate.equals(pinnedSslCertificateDateArray[1])) {
494                     endDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
495                 } else {
496                     endDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
497                 }
498             }
499
500             // Display the strings.
501             domainNameTextView.setText(domainNameStringBuilder);
502             ipAddressesTextView.setText(ipAddressesStringBuilder);
503             issuedToCNameTextView.setText(issuedToCNameStringBuilder);
504             issuedToONameTextView.setText(issuedToONameStringBuilder);
505             issuedToUNameTextView.setText(issuedToUNameStringBuilder);
506             issuedByCNameTextView.setText(issuedByCNameStringBuilder);
507             issuedByONameTextView.setText(issuedByONameStringBuilder);
508             issuedByUNameTextView.setText(issuedByUNameStringBuilder);
509             startDateTextView.setText(startDateStringBuilder);
510             endDateTextView.setText(endDateStringBuilder);
511
512             // Display the tab.
513             container.addView(tabViewGroup);
514
515             // Make it so.
516             return tabViewGroup;
517         }
518     }
519 }