2 * Copyright © 2021-2022 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
6 * Privacy Browser Android 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.
11 * Privacy Browser Android 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.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Browser Android. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacybrowser.adapters
22 import android.content.Context
23 import android.net.Uri
24 import android.text.SpannableStringBuilder
25 import android.text.Spanned
26 import android.text.style.ForegroundColorSpan
27 import android.view.LayoutInflater
28 import android.view.View
29 import android.view.ViewGroup
30 import android.widget.TextView
32 import androidx.viewpager.widget.PagerAdapter
34 import com.stoutner.privacybrowser.R
35 import com.stoutner.privacybrowser.activities.MainWebViewActivity
36 import com.stoutner.privacybrowser.views.NestedScrollWebView
38 import java.text.DateFormat
41 // This adapter uses a PagerAdapter instead of a FragmentPagerAdapter because dialogs fragments really don't like having a nested FragmentPagerAdapter inside of them.
42 class PinnedMismatchPagerAdapter(private val context: Context, private val layoutInflater: LayoutInflater, private val webViewFragmentId: Long) : PagerAdapter() {
43 override fun isViewFromObject(view: View, `object`: Any): Boolean {
44 // Check to see if the view and the object are the same.
45 return view === `object`
48 // Get the number of tabs.
49 override fun getCount(): Int {
50 // There are two tabs.
54 // Get the name of each tab. Tab numbers start at 0.
55 override fun getPageTitle(tabNumber: Int): CharSequence {
56 return when (tabNumber) {
57 0 -> context.getString(R.string.current)
58 1 -> context.getString(R.string.pinned)
64 override fun instantiateItem(container: ViewGroup, tabNumber: Int): Any {
65 // Get the current position of this WebView fragment.
66 val webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(webViewFragmentId)
68 // Get the WebView tab fragment.
69 val webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition)
71 // Get the WebView fragment view.
72 val webViewFragmentView = webViewTabFragment.requireView()
74 // Get a handle for the current WebView.
75 val nestedScrollWebView = webViewFragmentView.findViewById<NestedScrollWebView>(R.id.nestedscroll_webview)!!
77 // Inflate the scroll view for this tab.
78 val tabLayout = layoutInflater.inflate(R.layout.pinned_mismatch_tab_linearlayout, container, false) as ViewGroup
80 // Get handles for the views.
81 val domainNameTextView = tabLayout.findViewById<TextView>(R.id.domain_name)
82 val ipAddressesTextView = tabLayout.findViewById<TextView>(R.id.ip_addresses)
83 val issuedToCNameTextView = tabLayout.findViewById<TextView>(R.id.issued_to_cname)
84 val issuedToONameTextView = tabLayout.findViewById<TextView>(R.id.issued_to_oname)
85 val issuedToUNameTextView = tabLayout.findViewById<TextView>(R.id.issued_to_uname)
86 val issuedByCNameTextView = tabLayout.findViewById<TextView>(R.id.issued_by_cname)
87 val issuedByONameTextView = tabLayout.findViewById<TextView>(R.id.issued_by_oname)
88 val issuedByUNameTextView = tabLayout.findViewById<TextView>(R.id.issued_by_uname)
89 val startDateTextView = tabLayout.findViewById<TextView>(R.id.start_date)
90 val endDateTextView = tabLayout.findViewById<TextView>(R.id.end_date)
93 val domainNameLabel = context.getString(R.string.domain_label) + " "
94 val ipAddressesLabel = context.getString(R.string.ip_addresses) + " "
95 val cNameLabel = context.getString(R.string.common_name) + " "
96 val oNameLabel = context.getString(R.string.organization) + " "
97 val uNameLabel = context.getString(R.string.organizational_unit) + " "
98 val startDateLabel = context.getString(R.string.start_date) + " "
99 val endDateLabel = context.getString(R.string.end_date) + " "
101 // Convert the URL to a URI.
102 val currentUri = Uri.parse(nestedScrollWebView.url)
104 // Get the current host from the URI.
105 val domainName = currentUri.host
107 // Get the current website SSL certificate.
108 val sslCertificate = nestedScrollWebView.certificate
110 // Initialize the SSL certificate variables.
111 var currentSslIssuedToCName = ""
112 var currentSslIssuedToOName = ""
113 var currentSslIssuedToUName = ""
114 var currentSslIssuedByCName = ""
115 var currentSslIssuedByOName = ""
116 var currentSslIssuedByUName = ""
117 var currentSslStartDate: Date? = null
118 var currentSslEndDate: Date? = null
120 // Extract the individual pieces of information from the current website SSL certificate if it is not null.
121 if (sslCertificate != null) {
122 currentSslIssuedToCName = sslCertificate.issuedTo.cName
123 currentSslIssuedToOName = sslCertificate.issuedTo.oName
124 currentSslIssuedToUName = sslCertificate.issuedTo.uName
125 currentSslIssuedByCName = sslCertificate.issuedBy.cName
126 currentSslIssuedByOName = sslCertificate.issuedBy.oName
127 currentSslIssuedByUName = sslCertificate.issuedBy.uName
128 currentSslStartDate = sslCertificate.validNotBeforeDate
129 currentSslEndDate = sslCertificate.validNotAfterDate
132 // Get the pinned SSL certificate pair.
133 val pinnedSslCertificatePair = nestedScrollWebView.getPinnedSslCertificate()
135 // Extract the arrays from the array list.
136 val pinnedSslCertificateStringArray = pinnedSslCertificatePair.first
137 val pinnedSslCertificateDateArray = pinnedSslCertificatePair.second
139 // Setup the domain name spannable string builder.
140 val domainNameStringBuilder = SpannableStringBuilder(domainNameLabel + domainName)
142 // Initialize the spannable string builders.
143 val ipAddressesStringBuilder: SpannableStringBuilder
144 val issuedToCNameStringBuilder: SpannableStringBuilder
145 val issuedToONameStringBuilder: SpannableStringBuilder
146 val issuedToUNameStringBuilder: SpannableStringBuilder
147 val issuedByCNameStringBuilder: SpannableStringBuilder
148 val issuedByONameStringBuilder: SpannableStringBuilder
149 val issuedByUNameStringBuilder: SpannableStringBuilder
150 val startDateStringBuilder: SpannableStringBuilder
151 val endDateStringBuilder: SpannableStringBuilder
153 // Setup the spannable string builders for each tab.
154 if (tabNumber == 0) { // Setup the current settings tab.
155 // Create the string builders.
156 ipAddressesStringBuilder = SpannableStringBuilder(ipAddressesLabel + nestedScrollWebView.currentIpAddresses)
157 issuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + currentSslIssuedToCName)
158 issuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + currentSslIssuedToOName)
159 issuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + currentSslIssuedToUName)
160 issuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + currentSslIssuedByCName)
161 issuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + currentSslIssuedByOName)
162 issuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + currentSslIssuedByUName)
164 // Set the dates if they aren't null. Formatting a null date causes a crash.
165 startDateStringBuilder = if (currentSslStartDate == null) {
166 SpannableStringBuilder(startDateLabel)
168 SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslStartDate))
171 endDateStringBuilder = if (currentSslEndDate == null) {
172 SpannableStringBuilder(endDateLabel)
174 SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslEndDate))
176 } else { // Setup the pinned settings tab.
177 // Create the string builders.
178 ipAddressesStringBuilder = SpannableStringBuilder(ipAddressesLabel + nestedScrollWebView.pinnedIpAddresses)
179 issuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + pinnedSslCertificateStringArray[0])
180 issuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + pinnedSslCertificateStringArray[1])
181 issuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + pinnedSslCertificateStringArray[2])
182 issuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + pinnedSslCertificateStringArray[3])
183 issuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + pinnedSslCertificateStringArray[4])
184 issuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + pinnedSslCertificateStringArray[5])
185 startDateStringBuilder = SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[0]))
186 endDateStringBuilder = SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[1]))
189 // Create the color spans.
190 val blueColorSpan = ForegroundColorSpan(context.getColor(R.color.blue_text))
191 val redColorSpan = ForegroundColorSpan(context.getColor(R.color.red_text))
193 // Set the domain name to be blue.
194 domainNameStringBuilder.setSpan(blueColorSpan, domainNameLabel.length, domainNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
196 // Color coordinate the IP addresses if they are pinned.
197 if (nestedScrollWebView.pinnedIpAddresses != "") {
198 if (nestedScrollWebView.currentIpAddresses == nestedScrollWebView.pinnedIpAddresses) {
199 ipAddressesStringBuilder.setSpan(blueColorSpan, ipAddressesLabel.length, ipAddressesStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
201 ipAddressesStringBuilder.setSpan(redColorSpan, ipAddressesLabel.length, ipAddressesStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
205 // Color coordinate the SSL certificate fields if they are pinned.
206 if (nestedScrollWebView.hasPinnedSslCertificate()) {
207 if (currentSslIssuedToCName == pinnedSslCertificateStringArray[0]) {
208 issuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, issuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
210 issuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, issuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
213 if (currentSslIssuedToOName == pinnedSslCertificateStringArray[1]) {
214 issuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, issuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
216 issuedToONameStringBuilder.setSpan(redColorSpan, oNameLabel.length, issuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
219 if (currentSslIssuedToUName == pinnedSslCertificateStringArray[2]) {
220 issuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, issuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
222 issuedToUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length, issuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
225 if (currentSslIssuedByCName == pinnedSslCertificateStringArray[3]) {
226 issuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, issuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
228 issuedByCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, issuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
231 if (currentSslIssuedByOName == pinnedSslCertificateStringArray[4]) {
232 issuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, issuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
234 issuedByONameStringBuilder.setSpan(redColorSpan, oNameLabel.length, issuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
237 if (currentSslIssuedByUName == pinnedSslCertificateStringArray[5]) {
238 issuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, issuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
240 issuedByUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length, issuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
243 if (currentSslStartDate != null && currentSslStartDate == pinnedSslCertificateDateArray[0]) {
244 startDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, startDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
246 startDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, startDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
249 if (currentSslEndDate != null && currentSslEndDate == pinnedSslCertificateDateArray[1]) {
250 endDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, endDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
252 endDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, endDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
256 // Display the strings.
257 domainNameTextView.text = domainNameStringBuilder
258 ipAddressesTextView.text = ipAddressesStringBuilder
259 issuedToCNameTextView.text = issuedToCNameStringBuilder
260 issuedToONameTextView.text = issuedToONameStringBuilder
261 issuedToUNameTextView.text = issuedToUNameStringBuilder
262 issuedByCNameTextView.text = issuedByCNameStringBuilder
263 issuedByONameTextView.text = issuedByONameStringBuilder
264 issuedByUNameTextView.text = issuedByUNameStringBuilder
265 startDateTextView.text = startDateStringBuilder
266 endDateTextView.text = endDateStringBuilder
268 // Add the tab layout to the container. This needs to be manually done for pager adapters.
269 container.addView(tabLayout)
271 // Return the tab layout.