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