]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/commitdiff
Fix a crash if the View SSL Certificate is displayed when the app restarts. https...
authorSoren Stoutner <soren@stoutner.com>
Sat, 17 Apr 2021 22:48:59 +0000 (15:48 -0700)
committerSoren Stoutner <soren@stoutner.com>
Sat, 17 Apr 2021 22:48:59 +0000 (15:48 -0700)
app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcutDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/SslCertificateErrorDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.kt

index 32c4e51a20bf7545c146abc73bd0eef930799579..3e405c8d7af9162dc218883e2954da8815343352 100644 (file)
@@ -71,7 +71,7 @@ class CreateHomeScreenShortcutDialog : DialogFragment() {
             // Convert the favorite icon 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 byte array output stream to a byte array.
+            // Convert the favorite icon byte array output stream to a byte array.
             val favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray()
 
             // Create an arguments bundle.
@@ -107,14 +107,16 @@ class CreateHomeScreenShortcutDialog : DialogFragment() {
         // Convert the favorite icon byte array to a bitmap.
         val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
 
-        // Use an alert dialog builder to create the dialog.
-        val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
-
         // Create a drawable version of the favorite icon.
         val favoriteIconDrawable: Drawable = BitmapDrawable(resources, favoriteIconBitmap)
 
-        // Set the title and icon.
+        // Use an alert dialog builder to create the dialog.
+        val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
+
+        // Set the title.
         dialogBuilder.setTitle(R.string.create_shortcut)
+
+        // Set the icon.
         dialogBuilder.setIcon(favoriteIconDrawable)
 
         // Set the view.  The parent view is null because it will be assigned by the alert dialog.
index 139e6576e78b7055bfac1fa4786a2706f8bead0a..a4082552cf8354ccc093cb82fb5687723a52c412 100644 (file)
@@ -336,7 +336,6 @@ class SslCertificateErrorDialog : DialogFragment() {
                 primaryErrorString = getString(R.string.invalid_certificate)
         }
 
-
         // Display the strings.
         primaryErrorTextView.text = primaryErrorString
         urlTextView.text = urlStringBuilder
index 2bebf905107d537d5bc0b253a03f14c61552ae9e..3c971dc6bc712f6ca9b05a6ad1eea8acd7eab9e5 100644 (file)
@@ -22,6 +22,8 @@ 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
@@ -39,14 +41,47 @@ import androidx.preference.PreferenceManager
 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 HAS_SSL_CERTIFICATE = "has_ssl_certificate"
+private const val FAVORITE_ICON = "favorite_icon"
+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 favoriteIconDrawable: Drawable
+    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
@@ -71,23 +106,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
 
-        // Create a drawable version of the favorite icon.
-        val favoriteIconDrawable: Drawable = BitmapDrawable(resources, nestedScrollWebView.favoriteOrDefaultIcon)
+            // Create a drawable version of the favorite icon.
+            favoriteIconDrawable = BitmapDrawable(resources, nestedScrollWebView.favoriteOrDefaultIcon)
+
+            // 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)
+            val favoriteIconByteArray = savedInstanceState.getByteArray(FAVORITE_ICON)!!
+
+            // 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.
+            favoriteIconDrawable = BitmapDrawable(resources, favoriteIconBitmap)
+
+            // 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))
+            }
+        }
 
         // Set the icon.
         dialogBuilder.setIcon(favoriteIconDrawable)
@@ -95,9 +186,6 @@ 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)
 
@@ -105,25 +193,7 @@ class ViewSslCertificateDialog : DialogFragment() {
         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)
 
@@ -163,25 +233,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)
@@ -221,7 +275,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 +346,62 @@ 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.  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
+        }
+    }
+
+    override fun onSaveInstanceState(savedInstanceState: Bundle) {
+        // Run the default commands.
+        super.onSaveInstanceState(savedInstanceState)
+
+        // Get the favorite icon bitmap drawable.
+        val favoriteIconBitmapDrawable = favoriteIconDrawable as BitmapDrawable
+
+        // Get the favorite icon bitmap.
+        val favoriteIconBitmap = favoriteIconBitmapDrawable.bitmap
+
+        // 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()
+
+        // Save the common class variables.
+        savedInstanceState.putBoolean(HAS_SSL_CERTIFICATE, hasSslCertificate)
+        savedInstanceState.putByteArray(FAVORITE_ICON, favoriteIconByteArray)
+
+        // 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