X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Fadapters%2FPinnedMismatchPagerAdapter.kt;fp=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Fadapters%2FPinnedMismatchPagerAdapter.kt;h=6156533a72d2d4d1d50e4d846a5e58c4109bb1ab;hp=0000000000000000000000000000000000000000;hb=91154b307513e7bc6958b27fba518e4f9b564cf9;hpb=4ce562261f47e06c454504262a24f61f46bb393d diff --git a/app/src/main/java/com/stoutner/privacybrowser/adapters/PinnedMismatchPagerAdapter.kt b/app/src/main/java/com/stoutner/privacybrowser/adapters/PinnedMismatchPagerAdapter.kt new file mode 100644 index 00000000..6156533a --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/adapters/PinnedMismatchPagerAdapter.kt @@ -0,0 +1,302 @@ +/* + * Copyright © 2021 Soren Stoutner . + * + * This file is part of Privacy Browser . + * + * Privacy Browser is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Privacy Browser is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Privacy Browser. If not, see . + */ + +package com.stoutner.privacybrowser.adapters + +import android.content.Context +import android.content.res.Configuration +import android.net.Uri +import android.text.SpannableStringBuilder +import android.text.Spanned +import android.text.style.ForegroundColorSpan +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView + +import androidx.viewpager.widget.PagerAdapter + +import com.stoutner.privacybrowser.R +import com.stoutner.privacybrowser.activities.MainWebViewActivity +import com.stoutner.privacybrowser.views.NestedScrollWebView + +import java.text.DateFormat +import java.util.Date + +// This adapter uses a PagerAdapter instead of a FragmentPagerAdapter because dialogs fragments really don't like having a nested FragmentPagerAdapter inside of them. +class PinnedMismatchPagerAdapter(private val context: Context, private val layoutInflater: LayoutInflater, private val webViewFragmentId: Long) : PagerAdapter() { + override fun isViewFromObject(view: View, `object`: Any): Boolean { + // Check to see if the view and the object are the same. + return view === `object` + } + + // Get the number of tabs. + override fun getCount(): Int { + // There are two tabs. + return 2 + } + + // Get the name of each tab. Tab numbers start at 0. + override fun getPageTitle(tabNumber: Int): CharSequence { + return when (tabNumber) { + 0 -> context.getString(R.string.current) + 1 -> context.getString(R.string.pinned) + else -> "" + } + } + + // Setup each tab. + override fun instantiateItem(container: ViewGroup, tabNumber: Int): Any { + // Get the current position of this WebView fragment. + val webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(webViewFragmentId) + + // Get the WebView tab fragment. + val webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition) + + // Get the WebView fragment view. + val webViewFragmentView = webViewTabFragment.requireView() + + // Get a handle for the current WebView. + val nestedScrollWebView = webViewFragmentView.findViewById(R.id.nestedscroll_webview)!! + + // Inflate the scroll view for this tab. + val tabLayout = layoutInflater.inflate(R.layout.pinned_mismatch_tab_linearlayout, container, false) as ViewGroup + + // Get handles for the views. + val domainNameTextView = tabLayout.findViewById(R.id.domain_name) + val ipAddressesTextView = tabLayout.findViewById(R.id.ip_addresses) + val issuedToCNameTextView = tabLayout.findViewById(R.id.issued_to_cname) + val issuedToONameTextView = tabLayout.findViewById(R.id.issued_to_oname) + val issuedToUNameTextView = tabLayout.findViewById(R.id.issued_to_uname) + val issuedByCNameTextView = tabLayout.findViewById(R.id.issued_by_cname) + val issuedByONameTextView = tabLayout.findViewById(R.id.issued_by_oname) + val issuedByUNameTextView = tabLayout.findViewById(R.id.issued_by_uname) + val startDateTextView = tabLayout.findViewById(R.id.start_date) + val endDateTextView = tabLayout.findViewById(R.id.end_date) + + // Setup the labels. + val domainNameLabel = context.getString(R.string.domain_label) + " " + val ipAddressesLabel = context.getString(R.string.ip_addresses) + " " + val cNameLabel = context.getString(R.string.common_name) + " " + val oNameLabel = context.getString(R.string.organization) + " " + val uNameLabel = context.getString(R.string.organizational_unit) + " " + val startDateLabel = context.getString(R.string.start_date) + " " + val endDateLabel = context.getString(R.string.end_date) + " " + + // Convert the URL to a URI. + val currentUri = Uri.parse(nestedScrollWebView.url) + + // Get the current host from the URI. + val domainName = currentUri.host + + // Get the current website SSL certificate. + val sslCertificate = nestedScrollWebView.certificate + + // Initialize the SSL certificate variables. + var currentSslIssuedToCName = "" + var currentSslIssuedToOName = "" + var currentSslIssuedToUName = "" + var currentSslIssuedByCName = "" + var currentSslIssuedByOName = "" + var currentSslIssuedByUName = "" + var currentSslStartDate: Date? = null + var currentSslEndDate: Date? = null + + // Extract the individual pieces of information from the current website SSL certificate if it is not null. + if (sslCertificate != null) { + currentSslIssuedToCName = sslCertificate.issuedTo.cName + currentSslIssuedToOName = sslCertificate.issuedTo.oName + currentSslIssuedToUName = sslCertificate.issuedTo.uName + currentSslIssuedByCName = sslCertificate.issuedBy.cName + currentSslIssuedByOName = sslCertificate.issuedBy.oName + currentSslIssuedByUName = sslCertificate.issuedBy.uName + currentSslStartDate = sslCertificate.validNotBeforeDate + currentSslEndDate = sslCertificate.validNotAfterDate + } + + // Get the pinned SSL certificate. + val pinnedSslCertificateArrayList = nestedScrollWebView.pinnedSslCertificate + + // Extract the arrays from the array list. + val pinnedSslCertificateStringArray = pinnedSslCertificateArrayList[0] as Array<*> + val pinnedSslCertificateDateArray = pinnedSslCertificateArrayList[1] as Array<*> + + // Setup the domain name spannable string builder. + val domainNameStringBuilder = SpannableStringBuilder(domainNameLabel + domainName) + + // Initialize the spannable string builders. + val ipAddressesStringBuilder: SpannableStringBuilder + val issuedToCNameStringBuilder: SpannableStringBuilder + val issuedToONameStringBuilder: SpannableStringBuilder + val issuedToUNameStringBuilder: SpannableStringBuilder + val issuedByCNameStringBuilder: SpannableStringBuilder + val issuedByONameStringBuilder: SpannableStringBuilder + val issuedByUNameStringBuilder: SpannableStringBuilder + val startDateStringBuilder: SpannableStringBuilder + val endDateStringBuilder: SpannableStringBuilder + + // Setup the spannable string builders for each tab. + if (tabNumber == 0) { // Setup the current settings tab. + // Create the string builders. + ipAddressesStringBuilder = SpannableStringBuilder(ipAddressesLabel + nestedScrollWebView.currentIpAddresses) + issuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + currentSslIssuedToCName) + issuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + currentSslIssuedToOName) + issuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + currentSslIssuedToUName) + issuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + currentSslIssuedByCName) + issuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + currentSslIssuedByOName) + issuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + currentSslIssuedByUName) + + // Set the dates if they aren't null. Formatting a null date causes a crash. + startDateStringBuilder = if (currentSslStartDate == null) { + SpannableStringBuilder(startDateLabel) + } else { + SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslStartDate)) + } + + endDateStringBuilder = if (currentSslEndDate == null) { + SpannableStringBuilder(endDateLabel) + } else { + SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslEndDate)) + } + } else { // Setup the pinned settings tab. + // Create the string builders. + ipAddressesStringBuilder = SpannableStringBuilder(ipAddressesLabel + nestedScrollWebView.pinnedIpAddresses) + issuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + pinnedSslCertificateStringArray[0]) + issuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + pinnedSslCertificateStringArray[1]) + issuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + pinnedSslCertificateStringArray[2]) + issuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + pinnedSslCertificateStringArray[3]) + issuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + pinnedSslCertificateStringArray[4]) + issuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + pinnedSslCertificateStringArray[5]) + + // Set the dates if they aren't null. Formatting a null date causes a crash. + startDateStringBuilder = if (pinnedSslCertificateDateArray[0] == null) { + SpannableStringBuilder(startDateLabel) + } else { + SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[0])) + } + + endDateStringBuilder = if (pinnedSslCertificateDateArray[1] == null) { + SpannableStringBuilder(endDateLabel) + } else { + SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[1])) + } + } + + // Define the color spans. + val blueColorSpan: ForegroundColorSpan + val redColorSpan: ForegroundColorSpan + + // Get the current theme status. + val currentThemeStatus = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + + // Set the color spans according to the theme. The deprecated `resources` must be used until the minimum API >= 23. + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { + @Suppress("DEPRECATION") + blueColorSpan = ForegroundColorSpan(context.resources.getColor(R.color.blue_700)) + @Suppress("DEPRECATION") + redColorSpan = ForegroundColorSpan(context.resources.getColor(R.color.red_a700)) + } else { + @Suppress("DEPRECATION") + blueColorSpan = ForegroundColorSpan(context.resources.getColor(R.color.violet_700)) + @Suppress("DEPRECATION") + redColorSpan = ForegroundColorSpan(context.resources.getColor(R.color.red_900)) + } + + // Set the domain name to be blue. + domainNameStringBuilder.setSpan(blueColorSpan, domainNameLabel.length, domainNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + + // Color coordinate the IP addresses if they are pinned. + if (nestedScrollWebView.hasPinnedIpAddresses()) { + if (nestedScrollWebView.currentIpAddresses == nestedScrollWebView.pinnedIpAddresses) { + ipAddressesStringBuilder.setSpan(blueColorSpan, ipAddressesLabel.length, ipAddressesStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + ipAddressesStringBuilder.setSpan(redColorSpan, ipAddressesLabel.length, ipAddressesStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + } + + // Color coordinate the SSL certificate fields if they are pinned. + if (nestedScrollWebView.hasPinnedSslCertificate()) { + if (currentSslIssuedToCName == pinnedSslCertificateStringArray[0]) { + issuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, issuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + issuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, issuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + + if (currentSslIssuedToOName == pinnedSslCertificateStringArray[1]) { + issuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, issuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + issuedToONameStringBuilder.setSpan(redColorSpan, oNameLabel.length, issuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + + if (currentSslIssuedToUName == pinnedSslCertificateStringArray[2]) { + issuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, issuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + issuedToUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length, issuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + + if (currentSslIssuedByCName == pinnedSslCertificateStringArray[3]) { + issuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, issuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + issuedByCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, issuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + + if (currentSslIssuedByOName == pinnedSslCertificateStringArray[4]) { + issuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, issuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + issuedByONameStringBuilder.setSpan(redColorSpan, oNameLabel.length, issuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + + if (currentSslIssuedByUName == pinnedSslCertificateStringArray[5]) { + issuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, issuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + issuedByUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length, issuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + + if (currentSslStartDate != null && currentSslStartDate == pinnedSslCertificateDateArray[0]) { + startDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, startDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + startDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, startDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + + if (currentSslEndDate != null && currentSslEndDate == pinnedSslCertificateDateArray[1]) { + endDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, endDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + endDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, endDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + } + + // Display the strings. + domainNameTextView.text = domainNameStringBuilder + ipAddressesTextView.text = ipAddressesStringBuilder + issuedToCNameTextView.text = issuedToCNameStringBuilder + issuedToONameTextView.text = issuedToONameStringBuilder + issuedToUNameTextView.text = issuedToUNameStringBuilder + issuedByCNameTextView.text = issuedByCNameStringBuilder + issuedByONameTextView.text = issuedByONameStringBuilder + issuedByUNameTextView.text = issuedByUNameStringBuilder + startDateTextView.text = startDateStringBuilder + endDateTextView.text = endDateStringBuilder + + // Add the tab layout to the container. This needs to be manually done for pager adapters. + container.addView(tabLayout) + + // Return the tab layout. + return tabLayout + } +} \ No newline at end of file