]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.java
49c15394480b42ce96bbf74fe72c7cdfffe08cbf
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / dialogs / PinnedMismatchDialog.java
1 /*
2  * Copyright © 2017-2020 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.Dialog;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.SharedPreferences;
27 import android.content.res.Configuration;
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.appcompat.app.AlertDialog;
58 import androidx.core.content.ContextCompat;
59 import androidx.fragment.app.DialogFragment;  // The AndroidX dialog fragment must be used or an error is produced on API <=22.
60 import androidx.viewpager.widget.PagerAdapter;
61
62 public class PinnedMismatchDialog extends DialogFragment {
63     // The public interface is used to send information back to the parent activity.
64     public interface PinnedMismatchListener {
65         void pinnedErrorGoBack();
66     }
67
68     // Declare the class variables.
69     private PinnedMismatchListener pinnedMismatchListener;
70     private NestedScrollWebView nestedScrollWebView;
71     private String currentSslIssuedToCName;
72     private String currentSslIssuedToOName;
73     private String currentSslIssuedToUName;
74     private String currentSslIssuedByCName;
75     private String currentSslIssuedByOName;
76     private String currentSslIssuedByUName;
77     private Date currentSslStartDate;
78     private Date currentSslEndDate;
79
80     @Override
81     public void onAttach(@NonNull Context context) {
82         // Run the default commands.
83         super.onAttach(context);
84
85         // Get a handle for the listener from the launching context.
86         pinnedMismatchListener = (PinnedMismatchListener) context;
87     }
88
89     public static PinnedMismatchDialog displayDialog(long webViewFragmentId) {
90         // Create an arguments bundle.
91         Bundle argumentsBundle = new Bundle();
92
93         // Store the WebView fragment ID in the bundle.
94         argumentsBundle.putLong("webview_fragment_id", webViewFragmentId);
95
96         // Create a new instance of the pinned mismatch dialog.
97         PinnedMismatchDialog pinnedMismatchDialog = new PinnedMismatchDialog();
98
99         // Add the arguments bundle to the new instance.
100         pinnedMismatchDialog.setArguments(argumentsBundle);
101
102         // Make it so.
103         return pinnedMismatchDialog;
104     }
105
106     // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
107     @SuppressLint("InflateParams")
108     @Override
109     @NonNull
110     public Dialog onCreateDialog(Bundle savedInstanceState) {
111         // Get the arguments.
112         Bundle arguments = getArguments();
113
114         // Remove the incorrect lint warning below that `.getArguments().getInt()` might be null.
115         assert arguments != null;
116
117         // Get the current position of this WebView fragment.
118         int webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(arguments.getLong("webview_fragment_id"));
119
120         // Get the WebView tab fragment.
121         WebViewTabFragment webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition);
122
123         // Get the fragment view.
124         View fragmentView = webViewTabFragment.getView();
125
126         // Remove the incorrect lint warning below that the fragment view might be null.
127         assert fragmentView != null;
128
129         // Get a handle for the current WebView.
130         nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
131
132         // Use an alert dialog builder to create the alert dialog.
133         AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog);
134
135         // Get the context.
136         Context context = getContext();
137
138         // Remove the incorrect lint warning below that the context might be null.
139         assert context != null;
140
141         // Get the favorite icon.
142         Bitmap favoriteIconBitmap = nestedScrollWebView.getFavoriteOrDefaultIcon();
143
144         // Get the default favorite icon drawable.  `ContextCompat` must be used until API >= 21.
145         Drawable defaultFavoriteIconDrawable = ContextCompat.getDrawable(context, R.drawable.world);
146
147         // Cast the favorite icon drawable to a bitmap drawable.
148         BitmapDrawable defaultFavoriteIconBitmapDrawable = (BitmapDrawable) defaultFavoriteIconDrawable;
149
150         // Remove the incorrect warning below that the favorite icon bitmap drawable might be null.
151         assert defaultFavoriteIconBitmapDrawable != null;
152
153         // Store the default icon bitmap.
154         Bitmap defaultFavoriteIconBitmap = defaultFavoriteIconBitmapDrawable.getBitmap();
155
156         // Set the favorite icon as the dialog icon if it exists.
157         if (favoriteIconBitmap.sameAs(defaultFavoriteIconBitmap)) {  // There is no website favorite icon.
158             // Get the current theme status.
159             int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
160
161             // Set the icon according to the theme.
162             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
163                 dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_night);
164             } else {
165                 dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_day);
166             }
167         } else {  // There is a favorite icon.
168             // Create a drawable version of the favorite icon.
169             Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), favoriteIconBitmap);
170
171             // Set the icon.
172             dialogBuilder.setIcon(favoriteIconDrawable);
173         }
174
175         // Setup the neutral button.
176         dialogBuilder.setNeutralButton(R.string.update, (DialogInterface dialog, int which) -> {
177             // Initialize the long date variables.  If the date is null, a long value of `0` will be stored in the Domains database entry.
178             long currentSslStartDateLong = 0;
179             long currentSslEndDateLong = 0;
180
181             // Convert the `Dates` into `longs`.
182             if (currentSslStartDate != null) {
183                 currentSslStartDateLong = currentSslStartDate.getTime();
184             }
185
186             if (currentSslEndDate != null) {
187                 currentSslEndDateLong = currentSslEndDate.getTime();
188             }
189
190             // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
191             DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0);
192
193             // Update the SSL certificate if it is pinned.
194             if (nestedScrollWebView.hasPinnedSslCertificate()) {
195                 // Update the pinned SSL certificate in the domain database.
196                 domainsDatabaseHelper.updatePinnedSslCertificate(nestedScrollWebView.getDomainSettingsDatabaseId(), currentSslIssuedToCName, currentSslIssuedToOName, currentSslIssuedToUName,
197                         currentSslIssuedByCName, currentSslIssuedByOName, currentSslIssuedByUName, currentSslStartDateLong, currentSslEndDateLong);
198
199                 // Update the pinned SSL certificate in the nested scroll WebView.
200                 nestedScrollWebView.setPinnedSslCertificate(currentSslIssuedToCName, currentSslIssuedToOName, currentSslIssuedToUName, currentSslIssuedByCName, currentSslIssuedByOName, currentSslIssuedByUName,
201                         currentSslStartDate, currentSslEndDate);
202             }
203
204             // Update the IP addresses if they are pinned.
205             if (nestedScrollWebView.hasPinnedIpAddresses()) {
206                 // Update the pinned IP addresses in the domain database.
207                 domainsDatabaseHelper.updatePinnedIpAddresses(nestedScrollWebView.getDomainSettingsDatabaseId(), nestedScrollWebView.getCurrentIpAddresses());
208
209                 // Update the pinned IP addresses in the nested scroll WebView.
210                 nestedScrollWebView.setPinnedIpAddresses(nestedScrollWebView.getCurrentIpAddresses());
211             }
212         });
213
214         // Setup the back button.
215         dialogBuilder.setNegativeButton(R.string.back, (DialogInterface dialog, int which) -> {
216             if (nestedScrollWebView.canGoBack()) {  // There is a back page in the history.
217                 // Invoke the navigate history listener in the calling activity.  These commands cannot be run here because they need access to `applyDomainSettings()`.
218                 pinnedMismatchListener.pinnedErrorGoBack();
219             } else {  // There are no pages to go back to.
220                 // Load a blank page
221                 nestedScrollWebView.loadUrl("");
222             }
223         });
224
225         // Setup the proceed button.
226         dialogBuilder.setPositiveButton(R.string.proceed, (DialogInterface dialog, int which) -> {
227             // Do not check the pinned information for this domain again until the domain changes.
228             nestedScrollWebView.setIgnorePinnedDomainInformation(true);
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         // Get a handle for the shared preferences.
245         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
246
247         // Get the screenshot preference.
248         boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
249
250         // Disable screenshots if not allowed.
251         if (!allowScreenshots) {
252             // Remove the warning below that `getWindow()` might be null.
253             assert alertDialog.getWindow() != null;
254
255             // Disable screenshots.
256             alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
257         }
258
259         // Show the alert dialog so the items in the layout can be modified.
260         alertDialog.show();
261
262         //  Get a handle for the views.
263         WrapVerticalContentViewPager wrapVerticalContentViewPager = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_viewpager);
264         TabLayout tabLayout = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_tablayout);
265
266         // Remove the incorrect lint warning below that the views might be null.
267         assert wrapVerticalContentViewPager != null;
268         assert tabLayout != null;
269
270         // Set the view pager adapter.
271         wrapVerticalContentViewPager.setAdapter(new pagerAdapter());
272
273         // Connect the tab layout to the view pager.
274         tabLayout.setupWithViewPager(wrapVerticalContentViewPager);
275
276         // Return the alert dialog.
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             // Define the color spans.
428             ForegroundColorSpan blueColorSpan;
429             ForegroundColorSpan redColorSpan;
430
431             // Get the current theme status.
432             int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
433
434             // Set the color spans according to the theme.  The deprecated `getResources()` must be used until the minimum API >= 23.
435             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
436                 blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.violet_500));
437                 redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_900));
438             } else {
439                 blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
440                 redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
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 }