X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Fdialogs%2FPinnedMismatchDialog.kt;fp=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Fdialogs%2FPinnedMismatchDialog.kt;h=0dd2ce57a9b82f8ba43b4c9aaf66fcff841bb907;hp=0000000000000000000000000000000000000000;hb=91154b307513e7bc6958b27fba518e4f9b564cf9;hpb=4ce562261f47e06c454504262a24f61f46bb393d diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.kt new file mode 100644 index 00000000..0dd2ce57 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.kt @@ -0,0 +1,234 @@ +/* + * Copyright © 2017-2021 Soren Stoutner . + * + * This file is part of Privacy Browser . + * + * Privacy Browser 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, + * 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 . + */ + +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 + +import com.google.android.material.tabs.TabLayout + +import com.stoutner.privacybrowser.R +import com.stoutner.privacybrowser.activities.MainWebViewActivity +import com.stoutner.privacybrowser.adapters.PinnedMismatchPagerAdapter +import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper +import com.stoutner.privacybrowser.views.NestedScrollWebView + +// Declare the class constants. +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() + + // Store the WebView fragment ID in the bundle. + argumentsBundle.putLong(WEBVIEW_FRAGMENT_ID, webViewFragmentId) + + // Create a new instance of the pinned mismatch dialog. + val pinnedMismatchDialog = PinnedMismatchDialog() + + // Add the arguments bundle to the new instance. + pinnedMismatchDialog.arguments = argumentsBundle + + // Make it so. + return pinnedMismatchDialog + } + } + + // `@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) + + // Get the fragment view. + val fragmentView = webViewTabFragment.requireView() + + // Get a handle for the current WebView. + val nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview)!! + + // Use an alert dialog builder to create the alert dialog. + val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog) + + // Get the favorite icon. + val favoriteIconBitmap = nestedScrollWebView.favoriteOrDefaultIcon + + // Get the default favorite icon drawable. `ContextCompat` must be used until API >= 21. + val defaultFavoriteIconDrawable = ContextCompat.getDrawable(requireContext(), R.drawable.world) + + // Cast the favorite icon drawable to a bitmap drawable. + val defaultFavoriteIconBitmapDrawable = (defaultFavoriteIconDrawable as BitmapDrawable) + + // Store the default icon bitmap. + val defaultFavoriteIconBitmap = defaultFavoriteIconBitmapDrawable.bitmap + + // 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 + + // 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) + + // Set the icon. + dialogBuilder.setIcon(favoriteIconDrawable) + } + + // 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(requireActivity().layoutInflater.inflate(R.layout.pinned_mismatch_linearlayout, null)) + + // 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 + + // 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) + + // 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, + 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 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 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) + } + + // 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 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) + } + + // 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)!! + + // Initialize the pinned mismatch pager adapter. + val pinnedMismatchPagerAdapter = PinnedMismatchPagerAdapter(requireContext(), layoutInflater, webViewFragmentId) + + // Set the view pager adapter. + viewPager.adapter = pinnedMismatchPagerAdapter + + // Connect the tab layout to the view pager. + tabLayout.setupWithViewPager(viewPager) + + // Return the alert dialog. + return alertDialog + } +} \ No newline at end of file