X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Fdialogs%2FPinnedMismatchDialog.kt;h=56ec5192a2d4c35cb26b4e4075b8be6c122a6eda;hp=c3a5c7fed3034a741b36014982cf270aaa9fed18;hb=HEAD;hpb=031def95c6d9bfc14113fe86b4a5690233d93ce2 diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.kt index c3a5c7fe..c345fc5c 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.kt @@ -1,36 +1,31 @@ /* - * Copyright © 2017-2021 Soren Stoutner . + * Copyright 2017-2023 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.Context import android.content.DialogInterface -import android.content.res.Configuration -import android.graphics.drawable.BitmapDrawable -import android.graphics.drawable.Drawable import android.os.Bundle import android.view.WindowManager import androidx.appcompat.app.AlertDialog -import androidx.core.content.ContextCompat import androidx.fragment.app.DialogFragment import androidx.preference.PreferenceManager import androidx.viewpager.widget.ViewPager @@ -47,25 +42,7 @@ import com.stoutner.privacybrowser.views.NestedScrollWebView private const val WEBVIEW_FRAGMENT_ID = "webview_fragment_id" class PinnedMismatchDialog : DialogFragment() { - // Declare the class variables. - private lateinit var pinnedMismatchListener: PinnedMismatchListener - - // The public interface is used to send information back to the parent activity. - interface PinnedMismatchListener { - fun pinnedErrorGoBack() - } - - override fun onAttach(context: Context) { - // Run the default commands. - super.onAttach(context) - - // Get a handle for the listener from the launching context. - pinnedMismatchListener = context as PinnedMismatchListener - } - companion object { - // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. Also, the function can then be moved out of a companion object and just become a package-level function. - @JvmStatic fun displayDialog(webViewFragmentId: Long): PinnedMismatchDialog { // Create an arguments bundle. val argumentsBundle = Bundle() @@ -84,151 +61,149 @@ class PinnedMismatchDialog : 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 WebView fragment ID. - val webViewFragmentId = requireArguments().getLong(WEBVIEW_FRAGMENT_ID) - - // 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) + // Declare the class variables. + private lateinit var pinnedMismatchListener: PinnedMismatchListener - // Get the fragment view. - val fragmentView = webViewTabFragment.requireView() + // The public interface is used to send information back to the parent activity. + interface PinnedMismatchListener { + fun pinnedErrorGoBack() + } - // Get a handle for the current WebView. - val nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview)!! + override fun onAttach(context: Context) { + // Run the default commands. + super.onAttach(context) - // Use an alert dialog builder to create the alert dialog. - val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog) + // Get a handle for the listener from the launching context. + pinnedMismatchListener = context as PinnedMismatchListener + } - // Get the favorite icon. - val favoriteIconBitmap = nestedScrollWebView.favoriteOrDefaultIcon + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + // Try to create the dialog. This will fail if the app was restarted while the dialog is shown because the WebView view pager will not have been populated yet. + try { + // Get the WebView fragment ID. + val webViewFragmentId = requireArguments().getLong(WEBVIEW_FRAGMENT_ID) - // Get the default favorite icon drawable. `ContextCompat` must be used until API >= 21. - val defaultFavoriteIconDrawable = ContextCompat.getDrawable(requireContext(), R.drawable.world) + // Get the current position of this WebView fragment. + val webViewPosition = MainWebViewActivity.webViewStateAdapter!!.getPositionForId(webViewFragmentId) - // Cast the favorite icon drawable to a bitmap drawable. - val defaultFavoriteIconBitmapDrawable = (defaultFavoriteIconDrawable as BitmapDrawable) + // Get the WebView tab fragment. + val webViewTabFragment = MainWebViewActivity.webViewStateAdapter!!.getPageFragment(webViewPosition) - // Store the default icon bitmap. - val defaultFavoriteIconBitmap = defaultFavoriteIconBitmapDrawable.bitmap + // Get the fragment view. + val fragmentView = webViewTabFragment.requireView() - // Set the favorite icon as the dialog icon if it exists. - if (favoriteIconBitmap.sameAs(defaultFavoriteIconBitmap)) { // There is no website favorite icon. - // Get the current theme status. - val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + // Get a handle for the current WebView. + val nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview)!! - // Set the icon according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_day) - } else { - dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_night) - } - } else { // There is a favorite icon. - // Create a drawable version of the favorite icon. - val favoriteIconDrawable: Drawable = BitmapDrawable(resources, favoriteIconBitmap) + // Use an alert dialog builder to create the alert dialog. + val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog) // Set the icon. - dialogBuilder.setIcon(favoriteIconDrawable) - } + dialogBuilder.setIcon(R.drawable.ssl_certificate) - // Set the title. - dialogBuilder.setTitle(R.string.pinned_mismatch) + // Set the title. + dialogBuilder.setTitle(R.string.pinned_mismatch) - // Set the layout. The parent view is `null` because it will be assigned by the alert dialog. - dialogBuilder.setView(layoutInflater.inflate(R.layout.pinned_mismatch_linearlayout, null)) + // Set the layout. + dialogBuilder.setView(R.layout.pinned_mismatch_linearlayout) - // Set the update button listener. - dialogBuilder.setNeutralButton(R.string.update) { _: DialogInterface?, _: Int -> - // Get the current SSL certificate. - val currentSslCertificate = nestedScrollWebView.certificate!! + // Set the update button listener. + dialogBuilder.setNeutralButton(R.string.update) { _: DialogInterface?, _: Int -> + // Get the current SSL certificate. + val currentSslCertificate = nestedScrollWebView.certificate!! - // Get the dates from the certificate. - val currentSslStartDate = currentSslCertificate.validNotBeforeDate - val currentSslEndDate = currentSslCertificate.validNotAfterDate + // Get the dates from the certificate. + val currentSslStartDate = currentSslCertificate.validNotBeforeDate + val currentSslEndDate = currentSslCertificate.validNotAfterDate - // Convert the dates into longs. If the date is null, a long value of `0` will be stored in the domains database entry. - val currentSslStartDateLong: Long = currentSslStartDate?.time ?: 0 - val currentSslEndDateLong: Long = currentSslEndDate?.time ?: 0 + // Convert the dates into longs. If the date is null, a long value of `0` will be stored in the domains database entry. + val currentSslStartDateLong: Long = currentSslStartDate?.time ?: 0 + val currentSslEndDateLong: Long = currentSslEndDate?.time ?: 0 - // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in the domains database helper. - val domainsDatabaseHelper = DomainsDatabaseHelper(context, null, null, 0) + // Initialize the database handler. + val domainsDatabaseHelper = DomainsDatabaseHelper(requireContext()) - // Update the SSL certificate if it is pinned. - if (nestedScrollWebView.hasPinnedSslCertificate()) { - // Update the pinned SSL certificate in the domain database. - domainsDatabaseHelper.updatePinnedSslCertificate(nestedScrollWebView.domainSettingsDatabaseId, currentSslCertificate.issuedTo.cName, currentSslCertificate.issuedTo.oName, + // Update the SSL certificate if it is pinned. + if (nestedScrollWebView.hasPinnedSslCertificate()) { + // Update the pinned SSL certificate in the domain database. + domainsDatabaseHelper.updatePinnedSslCertificate(nestedScrollWebView.domainSettingsDatabaseId, currentSslCertificate.issuedTo.cName, currentSslCertificate.issuedTo.oName, currentSslCertificate.issuedTo.uName, currentSslCertificate.issuedBy.cName, currentSslCertificate.issuedBy.oName, currentSslCertificate.issuedBy.uName, currentSslStartDateLong, currentSslEndDateLong) - // Update the pinned SSL certificate in the nested scroll WebView. - nestedScrollWebView.setPinnedSslCertificate(currentSslCertificate.issuedTo.cName, currentSslCertificate.issuedTo.oName, currentSslCertificate.issuedTo.uName, + // Update the pinned SSL certificate in the nested scroll WebView. + nestedScrollWebView.setPinnedSslCertificate(currentSslCertificate.issuedTo.cName, currentSslCertificate.issuedTo.oName, currentSslCertificate.issuedTo.uName, currentSslCertificate.issuedBy.cName, currentSslCertificate.issuedBy.oName, currentSslCertificate.issuedBy.uName, currentSslStartDate, currentSslEndDate) - } + } - // Update the IP addresses if they are pinned. - if (nestedScrollWebView.hasPinnedIpAddresses()) { - // Update the pinned IP addresses in the domain database. - domainsDatabaseHelper.updatePinnedIpAddresses(nestedScrollWebView.domainSettingsDatabaseId, nestedScrollWebView. currentIpAddresses) + // Update the IP addresses if they are pinned. + if (nestedScrollWebView.pinnedIpAddresses != "") { + // Update the pinned IP addresses in the domain database. + domainsDatabaseHelper.updatePinnedIpAddresses(nestedScrollWebView.domainSettingsDatabaseId, nestedScrollWebView.currentIpAddresses) - // Update the pinned IP addresses in the nested scroll WebView. - nestedScrollWebView.pinnedIpAddresses = nestedScrollWebView.currentIpAddresses + // Update the pinned IP addresses in the nested scroll WebView. + nestedScrollWebView.pinnedIpAddresses = nestedScrollWebView.currentIpAddresses + } } - } - // Set the back button listener. - dialogBuilder.setNegativeButton(R.string.back) { _: DialogInterface?, _: Int -> - if (nestedScrollWebView.canGoBack()) { // There is a back page in the history. - // Invoke the navigate history listener in the calling activity. These commands cannot be run here because they need access to `applyDomainSettings()`. - pinnedMismatchListener.pinnedErrorGoBack() - } else { // There are no pages to go back to. - // Load a blank page - nestedScrollWebView.loadUrl("") + // Set the back button listener. + dialogBuilder.setNegativeButton(R.string.back) { _: DialogInterface?, _: Int -> + if (nestedScrollWebView.canGoBack()) { // There is a back page in the history. + // Invoke the navigate history listener in the calling activity. These commands cannot be run here because they need access to `applyDomainSettings()`. + pinnedMismatchListener.pinnedErrorGoBack() + } else { // There are no pages to go back to. + // Load a blank page + nestedScrollWebView.loadUrl("") + } } - } - // Set the proceed button listener. - dialogBuilder.setPositiveButton(R.string.proceed) { _: DialogInterface?, _: Int -> - // Do not check the pinned information for this domain again until the domain changes. - nestedScrollWebView.setIgnorePinnedDomainInformation(true) - } + // Set the proceed button listener. + dialogBuilder.setPositiveButton(R.string.proceed) { _: DialogInterface?, _: Int -> + // Do not check the pinned information for this domain again until the domain changes. + nestedScrollWebView.ignorePinnedDomainInformation = true + } - // Create an alert dialog from the alert dialog builder. - val alertDialog = dialogBuilder.create() + // Create an alert dialog from the alert dialog builder. + val alertDialog = dialogBuilder.create() - // Get a handle for the shared preferences. - val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + // Get a handle for the shared preferences. + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) - // Get the screenshot preference. - val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false) + // Get the screenshot preference. + val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false) - // Disable screenshots if not allowed. - if (!allowScreenshots) { - // Disable screenshots. - alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE) - } + // Disable screenshots if not allowed. + if (!allowScreenshots) { + // Disable screenshots. + alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + } + + // The alert dialog must be shown before items in the layout can be modified. + alertDialog.show() - // The alert dialog must be shown before items in the layout can be modified. - alertDialog.show() + // Get handles for the views. + val viewPager = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_viewpager)!! + val tabLayout = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_tablayout)!! - // Get handles for the views. - val viewPager = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_viewpager)!! - val tabLayout = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_tablayout)!! + // Initialize the pinned mismatch pager adapter. + val pinnedMismatchPagerAdapter = PinnedMismatchPagerAdapter(requireContext(), layoutInflater, webViewFragmentId) - // Initialize the pinned mismatch pager adapter. - val pinnedMismatchPagerAdapter = PinnedMismatchPagerAdapter(requireContext(), layoutInflater, webViewFragmentId) + // Set the view pager adapter. + viewPager.adapter = pinnedMismatchPagerAdapter - // Set the view pager adapter. - viewPager.adapter = pinnedMismatchPagerAdapter + // Connect the tab layout to the view pager. + tabLayout.setupWithViewPager(viewPager) - // Connect the tab layout to the view pager. - tabLayout.setupWithViewPager(viewPager) + // Return the alert dialog. + return alertDialog + } catch (exception: Exception) { // The app was restarted while the dialog was displayed. + // Dismiss this new instance of the dialog. Amazingly, the old instance will be restored by Android and, even more amazingly, will be fully functional. + dismiss() - // Return the alert dialog. - return alertDialog + // Use an alert dialog builder to create an empty alert dialog. + val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog) + + // Return the empty alert dialog. + return dialogBuilder.create() + } } -} \ No newline at end of file +}