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