X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Fdialogs%2FViewSslCertificateDialog.kt;h=0a79f80653c254a86641121f84412762fa7f6fb2;hp=2bebf905107d537d5bc0b253a03f14c61552ae9e;hb=1b27ac6f2b7c046945fc97e2aff9adbde8a152ce;hpb=031def95c6d9bfc14113fe86b4a5690233d93ce2 diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.kt index 2bebf905..0a79f806 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.kt @@ -1,29 +1,28 @@ /* - * Copyright © 2016-2021 Soren Stoutner . + * Copyright © 2016-2022 Soren Stoutner . * - * This file is part of Privacy Browser . + * This file is part of Privacy Browser Android . * - * Privacy Browser is free software: you can redistribute it and/or modify + * Privacy Browser Android 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, + * Privacy Browser Android 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 . + * along with Privacy Browser Android. If not, see . */ package com.stoutner.privacybrowser.dialogs -import android.annotation.SuppressLint import android.app.Dialog -import android.content.res.Configuration +import android.graphics.Bitmap +import android.graphics.BitmapFactory import android.graphics.drawable.BitmapDrawable -import android.graphics.drawable.Drawable import android.net.Uri import android.os.Bundle import android.text.SpannableStringBuilder @@ -40,22 +39,65 @@ import com.stoutner.privacybrowser.R import com.stoutner.privacybrowser.activities.MainWebViewActivity import com.stoutner.privacybrowser.views.NestedScrollWebView +import java.io.ByteArrayOutputStream + import java.text.DateFormat import java.util.Calendar +import java.util.Date // Define the class constants. private const val WEBVIEW_FRAGMENT_ID = "webview_fragment_id" +private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array" +private const val HAS_SSL_CERTIFICATE = "has_ssl_certificate" +private const val DOMAIN = "domain" +private const val IP_ADDRESSES = "ip_addresses" +private const val ISSUED_TO_CNAME = "issued_to_cname" +private const val ISSUED_TO_ONAME = "issued_to_oname" +private const val ISSUED_TO_UNAME = "issued_to_uname" +private const val ISSUED_BY_CNAME = "issued_by_cname" +private const val ISSUED_BY_ONAME = "issued_by_oname" +private const val ISSUED_BY_UNAME = "issued_by_uname" +private const val START_DATE = "start_date" +private const val END_DATE = "end_date" class ViewSslCertificateDialog : DialogFragment() { + // Define the class variables. + private var hasSslCertificate: Boolean = false + + // Declare the class variables. + private lateinit var domainString: String + private lateinit var ipAddresses: String + private lateinit var issuedToCName: String + private lateinit var issuedToOName: String + private lateinit var issuedToUName: String + private lateinit var issuedByCName: String + private lateinit var issuedByOName: String + private lateinit var issuedByUName: String + private lateinit var startDate: Date + private lateinit var endDate: Date + + // Declare the class views. + private lateinit var nestedScrollWebView: NestedScrollWebView + companion object { // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. @JvmStatic - fun displayDialog(webViewFragmentId: Long): ViewSslCertificateDialog { + fun displayDialog(webViewFragmentId: Long, favoriteIconBitmap: Bitmap): ViewSslCertificateDialog { // Create an arguments bundle. val argumentsBundle = Bundle() - // Store the WebView fragment ID in the bundle. + // Create a favorite icon byte array output stream. + val favoriteIconByteArrayOutputStream = ByteArrayOutputStream() + + // Convert the bitmap to a PNG and place it in the byte array output stream. `0` is for lossless compression (the only option for a PNG). + favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream) + + // Convert the favorite icon byte array output stream to a byte array. + val favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray() + + // Store the arguments in the bundle. argumentsBundle.putLong(WEBVIEW_FRAGMENT_ID, webViewFragmentId) + argumentsBundle.putByteArray(FAVORITE_ICON_BYTE_ARRAY, favoriteIconByteArray) // Create a new instance of the view SSL certificate dialog. val viewSslCertificateDialog = ViewSslCertificateDialog() @@ -68,26 +110,79 @@ class ViewSslCertificateDialog : DialogFragment() { } } - // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog. - @SuppressLint("InflateParams") override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - // Get the current position of this WebView fragment. - val webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(requireArguments().getLong(WEBVIEW_FRAGMENT_ID)) + // Use a builder to create the alert dialog. + val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog) - // Get the WebView tab fragment. - val webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition) + // Populate the class variables. + if (savedInstanceState == null) { // The dialog is starting for the first time. + // Get the current position of this WebView fragment. + val webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(requireArguments().getLong(WEBVIEW_FRAGMENT_ID)) - // Get the fragment view. - val fragmentView = webViewTabFragment.requireView() + // Get the WebView tab fragment. + val webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition) - // Get a handle for the current nested scroll WebView. - val nestedScrollWebView: NestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview) + // Get the fragment view. + val fragmentView = webViewTabFragment.requireView() - // Use a builder to create the alert dialog. - val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog) + // Get a handle for the current nested scroll WebView. + nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview) + + // Get the SSL certificate. + val sslCertificate = nestedScrollWebView.certificate + + // Store the status of the SSL certificate. + hasSslCertificate = sslCertificate != null + + // Populate the certificate class variables if the webpage has an SSL certificate. + if (hasSslCertificate) { + // Convert the URL to a URI. + val uri = Uri.parse(nestedScrollWebView.url) + + // Extract the domain name from the URI. + domainString = uri.host!! + + // Get the ip addresses from the nested scroll WebView. + ipAddresses = nestedScrollWebView.currentIpAddresses + + // Get the strings from the SSL certificate. + issuedToCName = sslCertificate!!.issuedTo.cName + issuedToOName = sslCertificate.issuedTo.oName + issuedToUName = sslCertificate.issuedTo.uName + issuedByCName = sslCertificate.issuedBy.cName + issuedByOName = sslCertificate.issuedBy.oName + issuedByUName = sslCertificate.issuedBy.uName + startDate = sslCertificate.validNotBeforeDate + endDate = sslCertificate.validNotAfterDate + } + } else { // The dialog has been restarted. + // Get the data from the saved instance state. + hasSslCertificate = savedInstanceState.getBoolean(HAS_SSL_CERTIFICATE) + + // Populate the certificate class variables if the webpage has an SSL certificate. + if (hasSslCertificate) { + // Populate the certificate class variables from the saved instance state. + domainString = savedInstanceState.getString(DOMAIN)!! + ipAddresses = savedInstanceState.getString(IP_ADDRESSES)!! + issuedToCName = savedInstanceState.getString(ISSUED_TO_CNAME)!! + issuedToOName = savedInstanceState.getString(ISSUED_TO_ONAME)!! + issuedToUName = savedInstanceState.getString(ISSUED_TO_UNAME)!! + issuedByCName = savedInstanceState.getString(ISSUED_BY_CNAME)!! + issuedByOName = savedInstanceState.getString(ISSUED_BY_ONAME)!! + issuedByUName = savedInstanceState.getString(ISSUED_BY_UNAME)!! + startDate = Date(savedInstanceState.getLong(START_DATE)) + endDate = Date(savedInstanceState.getLong(END_DATE)) + } + } + + // Get the favorite icon byte array from the arguments. + val favoriteIconByteArray = requireArguments().getByteArray(FAVORITE_ICON_BYTE_ARRAY)!! + + // Convert the favorite icon byte array to a bitmap. + val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size) // Create a drawable version of the favorite icon. - val favoriteIconDrawable: Drawable = BitmapDrawable(resources, nestedScrollWebView.favoriteOrDefaultIcon) + val favoriteIconDrawable = BitmapDrawable(resources, favoriteIconBitmap) // Set the icon. dialogBuilder.setIcon(favoriteIconDrawable) @@ -95,40 +190,19 @@ class ViewSslCertificateDialog : DialogFragment() { // Set the close button listener. Using `null` as the listener closes the dialog without doing anything else. dialogBuilder.setNegativeButton(R.string.close, null) - // Get the SSL certificate. - val sslCertificate = nestedScrollWebView.certificate - // Get a handle for the shared preferences. - val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) // Get the screenshot preference. val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false) // Check to see if the website is encrypted. - if (sslCertificate == null) { // The website is not encrypted. - // Set the title. - dialogBuilder.setTitle(R.string.unencrypted_website) - - // Set the Layout. The parent view is `null` because it will be assigned by the alert dialog. - dialogBuilder.setView(layoutInflater.inflate(R.layout.unencrypted_website_dialog, null)) - - // Create an alert dialog from the builder. - val alertDialog = dialogBuilder.create() - - // Disable screenshots if not allowed. - if (!allowScreenshots) { - // Disable screenshots. - alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE) - } - - // Return the alert dialog. - return alertDialog - } else { // The website is encrypted. + if (hasSslCertificate) { // The website is encrypted. // Set the title. dialogBuilder.setTitle(R.string.ssl_certificate) - // Set the layout. The parent view is `null` because it will be assigned by the alert dialog. - dialogBuilder.setView(layoutInflater.inflate(R.layout.view_ssl_certificate_dialog, null)) + // Set the layout. + dialogBuilder.setView(R.layout.view_ssl_certificate_dialog) // Create an alert dialog from the builder. val alertDialog = dialogBuilder.create() @@ -163,25 +237,9 @@ class ViewSslCertificateDialog : DialogFragment() { val startDateLabel = getString(R.string.start_date) + " " val endDateLabel = getString(R.string.end_date) + " " - // Convert the URL to a URI. - val uri = Uri.parse(nestedScrollWebView.url) - - // Extract the domain name from the URI. - val domainString = uri.host - - // Get the strings from the SSL certificate. - val issuedToCName = sslCertificate.issuedTo.cName - val issuedToOName = sslCertificate.issuedTo.oName - val issuedToUName = sslCertificate.issuedTo.uName - val issuedByCName = sslCertificate.issuedBy.cName - val issuedByOName = sslCertificate.issuedBy.oName - val issuedByUName = sslCertificate.issuedBy.uName - val startDate = sslCertificate.validNotBeforeDate - val endDate = sslCertificate.validNotAfterDate - // Create spannable string builders for each text view that needs multiple colors of text. val domainStringBuilder = SpannableStringBuilder(domainLabel + domainString) - val ipAddressesStringBuilder = SpannableStringBuilder(ipAddressesLabel + nestedScrollWebView.currentIpAddresses) + val ipAddressesStringBuilder = SpannableStringBuilder(ipAddressesLabel + ipAddresses) val issuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + issuedToCName) val issuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + issuedToOName) val issuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + issuedToUName) @@ -192,24 +250,8 @@ class ViewSslCertificateDialog : DialogFragment() { val endDateStringBuilder = SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(endDate)) // Define the color spans. - val blueColorSpan: ForegroundColorSpan - val redColorSpan: ForegroundColorSpan - - // Get the current theme status. - val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK - - // Set the color spans according to the theme. The deprecated `getColor()` must be used until the minimum API >= 23. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - @Suppress("DEPRECATION") - blueColorSpan = ForegroundColorSpan(resources.getColor(R.color.blue_700)) - @Suppress("DEPRECATION") - redColorSpan = ForegroundColorSpan(resources.getColor(R.color.red_a700)) - } else { - @Suppress("DEPRECATION") - blueColorSpan = ForegroundColorSpan(resources.getColor(R.color.violet_700)) - @Suppress("DEPRECATION") - redColorSpan = ForegroundColorSpan(resources.getColor(R.color.red_900)) - } + val blueColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.blue_text)) + val redColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.red_text)) // Format the domain string and issued to CName colors. if (domainString == issuedToCName) { // The domain and issued to CName match. @@ -221,7 +263,7 @@ class ViewSslCertificateDialog : DialogFragment() { val baseCertificateDomain = issuedToCName.substring(2) // Setup a copy of the domain string to test subdomains. - var domainStringSubdomain = domainString!! + var domainStringSubdomain = domainString // Define a domain names match variable. var domainNamesMatch = false @@ -292,6 +334,46 @@ class ViewSslCertificateDialog : DialogFragment() { // Return the alert dialog. return alertDialog + } else { // The website is not encrypted. + // Set the title. + dialogBuilder.setTitle(R.string.unencrypted_website) + + // Set the Layout. + dialogBuilder.setView(R.layout.unencrypted_website_dialog) + + // Create an alert dialog from the builder. + val alertDialog = dialogBuilder.create() + + // Disable screenshots if not allowed. + if (!allowScreenshots) { + // Disable screenshots. + alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + } + + // Return the alert dialog. + return alertDialog + } + } + + override fun onSaveInstanceState(savedInstanceState: Bundle) { + // Run the default commands. + super.onSaveInstanceState(savedInstanceState) + + // Save the common class variables. + savedInstanceState.putBoolean(HAS_SSL_CERTIFICATE, hasSslCertificate) + + // Save the SSL certificate strings if they exist. + if (hasSslCertificate) { + savedInstanceState.putString(DOMAIN, domainString) + savedInstanceState.putString(IP_ADDRESSES, ipAddresses) + savedInstanceState.putString(ISSUED_TO_CNAME, issuedToCName) + savedInstanceState.putString(ISSUED_TO_ONAME, issuedToOName) + savedInstanceState.putString(ISSUED_TO_UNAME, issuedToUName) + savedInstanceState.putString(ISSUED_BY_CNAME, issuedByCName) + savedInstanceState.putString(ISSUED_BY_ONAME, issuedByOName) + savedInstanceState.putString(ISSUED_BY_UNAME, issuedByUName) + savedInstanceState.putLong(START_DATE, startDate.time) + savedInstanceState.putLong(END_DATE, endDate.time) } } } \ No newline at end of file