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