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