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.
133 val pinnedSslCertificateArrayList = nestedScrollWebView.getPinnedSslCertificate()
135 // Extract the arrays from the array list.
136 val pinnedSslCertificateStringArray = pinnedSslCertificateArrayList[0] as Array<*>
137 val pinnedSslCertificateDateArray = pinnedSslCertificateArrayList[1] as Array<*>
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])
186 // Set the dates if they aren't null. Formatting a null date causes a crash.
187 startDateStringBuilder = if (pinnedSslCertificateDateArray[0] == null) {
188 SpannableStringBuilder(startDateLabel)
190 SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[0]))
193 endDateStringBuilder = if (pinnedSslCertificateDateArray[1] == null) {
194 SpannableStringBuilder(endDateLabel)
196 SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[1]))
200 // Create the color spans.
201 val blueColorSpan = ForegroundColorSpan(context.getColor(R.color.blue_text))
202 val redColorSpan = ForegroundColorSpan(context.getColor(R.color.red_text))
204 // Set the domain name to be blue.
205 domainNameStringBuilder.setSpan(blueColorSpan, domainNameLabel.length, domainNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
207 // Color coordinate the IP addresses if they are pinned.
208 if (nestedScrollWebView.pinnedIpAddresses != "") {
209 if (nestedScrollWebView.currentIpAddresses == nestedScrollWebView.pinnedIpAddresses) {
210 ipAddressesStringBuilder.setSpan(blueColorSpan, ipAddressesLabel.length, ipAddressesStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
212 ipAddressesStringBuilder.setSpan(redColorSpan, ipAddressesLabel.length, ipAddressesStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
216 // Color coordinate the SSL certificate fields if they are pinned.
217 if (nestedScrollWebView.hasPinnedSslCertificate()) {
218 if (currentSslIssuedToCName == pinnedSslCertificateStringArray[0]) {
219 issuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, issuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
221 issuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, issuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
224 if (currentSslIssuedToOName == pinnedSslCertificateStringArray[1]) {
225 issuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, issuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
227 issuedToONameStringBuilder.setSpan(redColorSpan, oNameLabel.length, issuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
230 if (currentSslIssuedToUName == pinnedSslCertificateStringArray[2]) {
231 issuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, issuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
233 issuedToUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length, issuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
236 if (currentSslIssuedByCName == pinnedSslCertificateStringArray[3]) {
237 issuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, issuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
239 issuedByCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, issuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
242 if (currentSslIssuedByOName == pinnedSslCertificateStringArray[4]) {
243 issuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, issuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
245 issuedByONameStringBuilder.setSpan(redColorSpan, oNameLabel.length, issuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
248 if (currentSslIssuedByUName == pinnedSslCertificateStringArray[5]) {
249 issuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, issuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
251 issuedByUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length, issuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
254 if (currentSslStartDate != null && currentSslStartDate == pinnedSslCertificateDateArray[0]) {
255 startDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, startDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
257 startDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, startDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
260 if (currentSslEndDate != null && currentSslEndDate == pinnedSslCertificateDateArray[1]) {
261 endDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, endDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
263 endDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, endDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
267 // Display the strings.
268 domainNameTextView.text = domainNameStringBuilder
269 ipAddressesTextView.text = ipAddressesStringBuilder
270 issuedToCNameTextView.text = issuedToCNameStringBuilder
271 issuedToONameTextView.text = issuedToONameStringBuilder
272 issuedToUNameTextView.text = issuedToUNameStringBuilder
273 issuedByCNameTextView.text = issuedByCNameStringBuilder
274 issuedByONameTextView.text = issuedByONameStringBuilder
275 issuedByUNameTextView.text = issuedByUNameStringBuilder
276 startDateTextView.text = startDateStringBuilder
277 endDateTextView.text = endDateStringBuilder
279 // Add the tab layout to the container. This needs to be manually done for pager adapters.
280 container.addView(tabLayout)
282 // Return the tab layout.