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