]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/commitdiff
Make pinned settings tab aware.
authorSoren Stoutner <soren@stoutner.com>
Wed, 3 Apr 2019 21:37:17 +0000 (14:37 -0700)
committerSoren Stoutner <soren@stoutner.com>
Wed, 3 Apr 2019 21:37:17 +0000 (14:37 -0700)
12 files changed:
app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/adapters/WebViewPagerAdapter.java
app/src/main/java/com/stoutner/privacybrowser/asynctasks/GetHostIpAddresses.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcutDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java
app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java
app/src/main/java/com/stoutner/privacybrowser/fragments/WebViewTabFragment.java
app/src/main/java/com/stoutner/privacybrowser/helpers/CheckPinnedMismatchHelper.java [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java
fastlane/metadata/android/ru-RU/full_description.txt

index 5026e727070a0fd45aaad5ee8ceb09c36c435a43..3f3b942889bb51267bc19a009798e5405fc37eee 100644 (file)
@@ -72,6 +72,11 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
     // `dismissingSnackbar` is public static so it can be accessed from `DomainsListFragment`.  It is also used in `onOptionsItemSelected()`.
     public static boolean dismissingSnackbar;
 
+    // The SSL certificate and current IP addresses are used to update pinned settings.
+    public static SslCertificate currentSslCertificate;
+    public static String currentIpAddresses;
+
+
     // `closeActivityAfterDismissingSnackbar` is used in `onOptionsItemSelected()`, and `onBackPressed()`.
     private boolean closeActivityAfterDismissingSnackbar;
 
@@ -719,18 +724,15 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
 
         // Update the pinned SSL certificate if a new one is checked.
         if (currentWebsiteCertificateRadioButton.isChecked()) {
-            // Get the current website SSL certificate.
-            SslCertificate currentWebsiteSslCertificate = MainWebViewActivity.sslCertificate;
-
             // Store the values from the SSL certificate.
-            String issuedToCommonName = currentWebsiteSslCertificate.getIssuedTo().getCName();
-            String issuedToOrganization = currentWebsiteSslCertificate.getIssuedTo().getOName();
-            String issuedToOrganizationalUnit = currentWebsiteSslCertificate.getIssuedTo().getUName();
-            String issuedByCommonName = currentWebsiteSslCertificate.getIssuedBy().getCName();
-            String issuedByOrganization = currentWebsiteSslCertificate.getIssuedBy().getOName();
-            String issuedByOrganizationalUnit = currentWebsiteSslCertificate.getIssuedBy().getUName();
-            long startDateLong = currentWebsiteSslCertificate.getValidNotBeforeDate().getTime();
-            long endDateLong = currentWebsiteSslCertificate.getValidNotAfterDate().getTime();
+            String issuedToCommonName = currentSslCertificate.getIssuedTo().getCName();
+            String issuedToOrganization = currentSslCertificate.getIssuedTo().getOName();
+            String issuedToOrganizationalUnit = currentSslCertificate.getIssuedTo().getUName();
+            String issuedByCommonName = currentSslCertificate.getIssuedBy().getCName();
+            String issuedByOrganization = currentSslCertificate.getIssuedBy().getOName();
+            String issuedByOrganizationalUnit = currentSslCertificate.getIssuedBy().getUName();
+            long startDateLong = currentSslCertificate.getValidNotBeforeDate().getTime();
+            long endDateLong = currentSslCertificate.getValidNotAfterDate().getTime();
 
             // Update the database.
             domainsDatabaseHelper.updatePinnedSslCertificate(currentDomainDatabaseId, issuedToCommonName, issuedToOrganization, issuedToOrganizationalUnit, issuedByCommonName, issuedByOrganization,
index e6ec1b71cebebd8dc39c490b79c1ba37d50cb656..fd357a6a4ed478550916b88ddc639026720590e0 100644 (file)
@@ -36,7 +36,6 @@ import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -133,6 +132,7 @@ import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
 import com.stoutner.privacybrowser.helpers.AdHelper;
 import com.stoutner.privacybrowser.helpers.BlockListHelper;
 import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
+import com.stoutner.privacybrowser.helpers.CheckPinnedMismatchHelper;
 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
 import com.stoutner.privacybrowser.helpers.OrbotProxyHelper;
 import com.stoutner.privacybrowser.views.NestedScrollWebView;
@@ -154,6 +154,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+// TODO.  The swipe refresh indicator needs to be enabled/disabled when switching tabs.
+
 // AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21.
 public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
         DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener,
@@ -174,21 +176,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `favoriteIconDefaultBitmap` public static so it can be accessed from `PinnedMismatchDialog`.  It is also used in `onCreate()` and `applyDomainSettings`.
     public static Bitmap favoriteIconDefaultBitmap;
 
+    // TODO Remove.
     // `formattedUrlString` is public static so it can be accessed from `AddDomainDialog`, `BookmarksActivity`, `DomainSettingsFragment`, and `PinnedMismatchDialog`.
     // It is also used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onCreateHomeScreenShortcutCreate()`, `loadUrlFromTextBox()`, and `applyProxyThroughOrbot()`.
     public static String formattedUrlString;
 
-    // `sslCertificate` is public static so it can be accessed from `DomainsActivity`, `DomainsListFragment`, `DomainSettingsFragment`, `PinnedMismatchDialog`, and `ViewSslCertificateDialog`.
-    // It is also used in `onCreate()` and `checkPinnedMismatch()`.
-    public static SslCertificate sslCertificate;
-
-    // `currentHostIpAddresses` is public static so it can be accessed from `DomainSettingsFragment`, `GetHostIpAddresses()`, and `ViewSslCertificateDialog`.
-    // It is also used in `onCreate()` and `GetHostIpAddresses()`.
-    public static String currentHostIpAddresses;
-
-    // The getting IP addresses tracker is used in `onCreate() and `GetHostIpAddresses`.
-    public static boolean gettingIpAddresses;
-
+    // TODO.  We are going to have to move this to the NestedScrollWebView.
     // The URL loading tracker is public static so it can be accessed from `GetHostIpAddresses`.
     // It is also used in `onCreate()`, `onCreateOptionsMenu()`, `loadUrl()`, `applyDomainSettings()`, and `GetHostIpAddresses`.
     public static boolean urlIsLoading;
@@ -196,9 +189,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`.  It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`.
     public static String orbotStatus;
 
+    // TODO.
     // `appliedUserAgentString` is public static so it can be accessed from `ViewSourceActivity`.  It is also used in `applyDomainSettings()`.
     public static String appliedUserAgentString;
 
+    // The WebView pager adapter is accessed from `PinnedMismatchDialog`.  It is also used in `onCreate()`, `onResume()`, and `addTab()`.
+    public static WebViewPagerAdapter webViewPagerAdapter;
+
     // `reloadOnRestart` is public static so it can be accessed from `SettingsFragment`.  It is also used in `onRestart()`
     public static boolean reloadOnRestart;
 
@@ -223,17 +220,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
     public static String currentBookmarksFolder;
 
-    // The pinned variables are public static so they can be accessed from `PinnedMismatchDialog`.  They are also used in `onCreate()`, `applyDomainSettings()`, and `checkPinnedMismatch()`.
-    public static String pinnedSslIssuedToCName;
-    public static String pinnedSslIssuedToOName;
-    public static String pinnedSslIssuedToUName;
-    public static String pinnedSslIssuedByCName;
-    public static String pinnedSslIssuedByOName;
-    public static String pinnedSslIssuedByUName;
-    public static Date pinnedSslStartDate;
-    public static Date pinnedSslEndDate;
-    public static String pinnedHostIpAddresses;
-
     // The user agent constants are public static so they can be accessed from `SettingsFragment`, `DomainsActivity`, and `DomainSettingsFragment`.
     public final static int UNRECOGNIZED_USER_AGENT = -1;
     public final static int SETTINGS_WEBVIEW_DEFAULT_USER_AGENT = 1;
@@ -244,22 +230,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
 
 
-    // `pinnedDomainSslCertificate` is used in `onCreate()`, `applyDomainSettings()`, and `checkPinnedMismatch()`.
-    private static boolean pinnedSslCertificate;
-
-    // `pinnedIpAddress` is used in `applyDomainSettings()` and `checkPinnedMismatch()`.
-    private static boolean pinnedIpAddresses;
-
-    // `ignorePinnedDomainInformation` is used in `onSslMismatchProceed()`, `applyDomainSettings()`, and `checkPinnedMismatch()`.
-    private static boolean ignorePinnedDomainInformation;
-
-    // The fragment manager is initialized in `onCreate()` and accessed from the static `checkPinnedMismatch()`.
-    private static FragmentManager fragmentManager;
-
-
-    // A handle for the activity is set in `onCreate()` and accessed in `WebViewPagerAdapter`.
-    private Activity activity;
-
     // `navigatingHistory` is used in `onCreate()`, `onNavigationItemSelected()`, `onSslMismatchBack()`, and `applyDomainSettings()`.
     private boolean navigatingHistory;
 
@@ -291,6 +261,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `saveFormDataEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.  It can be removed once the minimum API >= 26.
     private boolean saveFormDataEnabled;
 
+    // TODO Move to NestedScrollWebView.
     // `nightMode` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and  `applyDomainSettings()`.
     private boolean nightMode;
 
@@ -306,14 +277,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `refreshMenuItem` is used in `onCreate()` and `onCreateOptionsMenu()`.
     private MenuItem refreshMenuItem;
 
-    // The WebView pager adapter is used in `onCreate()`, `onResume()`, and `addTab()`.
-    private WebViewPagerAdapter webViewPagerAdapter;
-
     // The navigation requests menu item is used in `onCreate()` and accessed from `WebViewPagerAdapter`.
     private MenuItem navigationRequestsMenuItem;
 
+    // TODO.  This could probably be removed.
     // The blocklist helper is used in `onCreate()` and `WebViewPagerAdapter`.
-    BlockListHelper blockListHelper;
+    private BlockListHelper blockListHelper;
 
     // The blocklists are populated in `onCreate()` and accessed from `WebViewPagerAdapter`.
     private ArrayList<List<String[]>> easyList;
@@ -490,10 +459,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Set the content view.
         setContentView(R.layout.main_framelayout);
 
-        // Get handles for views, resources, and managers.
-        activity = this;
-        Resources resources = getResources();
-        fragmentManager = getSupportFragmentManager();
+        // Get handles for the input method manager and toolbar.
         inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
         Toolbar toolbar = findViewById(R.id.toolbar);
 
@@ -509,9 +475,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
 
         // Initialize the foreground color spans for highlighting the URLs.  We have to use the deprecated `getColor()` until API >= 23.
-        redColorSpan = new ForegroundColorSpan(resources.getColor(R.color.red_a700));
-        initialGrayColorSpan = new ForegroundColorSpan(resources.getColor(R.color.gray_500));
-        finalGrayColorSpan = new ForegroundColorSpan(resources.getColor(R.color.gray_500));
+        redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+        initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
+        finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
 
         // Get a handle for `urlTextBox`.
         EditText urlEditText = findViewById(R.id.url_edittext);
@@ -619,7 +585,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         navigationRequestsMenuItem = navigationMenu.getItem(6);
 
         // Initialize the web view pager adapter.
-        webViewPagerAdapter = new WebViewPagerAdapter(fragmentManager);
+        webViewPagerAdapter = new WebViewPagerAdapter(getSupportFragmentManager());
 
         // Set the pager adapter on the web view pager.
         webViewPager.setAdapter(webViewPagerAdapter);
@@ -710,7 +676,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             @Override
             public void onTabReselected(TabLayout.Tab tab) {
                 // Instantiate the View SSL Certificate dialog.
-                DialogFragment viewSslCertificateDialogFragment = new ViewSslCertificateDialog();
+                DialogFragment viewSslCertificateDialogFragment = ViewSslCertificateDialog.displayDialog(currentWebView.getWebViewFragmentId());
 
                 // Display the View SSL Certificate dialog.
                 viewSslCertificateDialogFragment.show(getSupportFragmentManager(), getString(R.string.view_ssl_certificate));
@@ -721,16 +687,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         addTab(webViewPager);
 
         // Set the bookmarks drawer resources according to the theme.  This can't be done in the layout due to compatibility issues with the `DrawerLayout` support widget.
+        // The deprecated `getResources().getDrawable()` must be used until the minimum API >= 21 and and `getResources().getColor()` must be used until the minimum API >= 23.
         if (darkTheme) {
-            launchBookmarksActivityFab.setImageDrawable(resources.getDrawable(R.drawable.bookmarks_dark));
-            createBookmarkFolderFab.setImageDrawable(resources.getDrawable(R.drawable.create_folder_dark));
-            createBookmarkFab.setImageDrawable(resources.getDrawable(R.drawable.create_bookmark_dark));
-            bookmarksListView.setBackgroundColor(resources.getColor(R.color.gray_850));
+            launchBookmarksActivityFab.setImageDrawable(getResources().getDrawable(R.drawable.bookmarks_dark));
+            createBookmarkFolderFab.setImageDrawable(getResources().getDrawable(R.drawable.create_folder_dark));
+            createBookmarkFab.setImageDrawable(getResources().getDrawable(R.drawable.create_bookmark_dark));
+            bookmarksListView.setBackgroundColor(getResources().getColor(R.color.gray_850));
         } else {
-            launchBookmarksActivityFab.setImageDrawable(resources.getDrawable(R.drawable.bookmarks_light));
-            createBookmarkFolderFab.setImageDrawable(resources.getDrawable(R.drawable.create_folder_light));
-            createBookmarkFab.setImageDrawable(resources.getDrawable(R.drawable.create_bookmark_light));
-            bookmarksListView.setBackgroundColor(resources.getColor(R.color.white));
+            launchBookmarksActivityFab.setImageDrawable(getResources().getDrawable(R.drawable.bookmarks_light));
+            createBookmarkFolderFab.setImageDrawable(getResources().getDrawable(R.drawable.create_folder_light));
+            createBookmarkFab.setImageDrawable(getResources().getDrawable(R.drawable.create_bookmark_light));
+            bookmarksListView.setBackgroundColor(getResources().getColor(R.color.white));
         }
 
         // Set the launch bookmarks activity FAB to launch the bookmarks activity.
@@ -753,7 +720,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         createBookmarkFolderFab.setOnClickListener(v -> {
             // Show the create bookmark folder dialog and name the instance `@string/create_folder`.
             DialogFragment createBookmarkFolderDialog = new CreateBookmarkFolderDialog();
-            createBookmarkFolderDialog.show(fragmentManager, resources.getString(R.string.create_folder));
+            createBookmarkFolderDialog.show(getSupportFragmentManager(), getString(R.string.create_folder));
         });
 
         // Set the create new bookmark FAB to display an alert dialog.
@@ -762,7 +729,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             DialogFragment createBookmarkDialog = CreateBookmarkDialog.createBookmark(currentWebView.getUrl(), currentWebView.getTitle(), favoriteIconBitmap);
 
             // Display the create bookmark dialog.
-            createBookmarkDialog.show(fragmentManager, resources.getString(R.string.create_bookmark));
+            createBookmarkDialog.show(getSupportFragmentManager(), getString(R.string.create_bookmark));
         });
 
         // Search for the string on the page whenever a character changes in the `findOnPageEditText`.
@@ -865,11 +832,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Show the edit bookmark folder `AlertDialog` and name the instance `@string/edit_folder`.
                 DialogFragment editBookmarkFolderDialog = EditBookmarkFolderDialog.folderDatabaseId(databaseId);
-                editBookmarkFolderDialog.show(fragmentManager, resources.getString(R.string.edit_folder));
+                editBookmarkFolderDialog.show(getSupportFragmentManager(), getString(R.string.edit_folder));
             } else {
                 // Show the edit bookmark `AlertDialog` and name the instance `@string/edit_bookmark`.
                 DialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId);
-                editBookmarkDialog.show(fragmentManager, resources.getString(R.string.edit_bookmark));
+                editBookmarkDialog.show(getSupportFragmentManager(), getString(R.string.edit_bookmark));
             }
 
             // Consume the event.
@@ -877,11 +844,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         });
 
         // Get the status bar pixel size.
-        int statusBarResourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
-        int statusBarPixelSize = resources.getDimensionPixelSize(statusBarResourceId);
+        int statusBarResourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
+        int statusBarPixelSize = getResources().getDimensionPixelSize(statusBarResourceId);
 
         // Get the resource density.
-        float screenDensity = resources.getDisplayMetrics().density;
+        float screenDensity = getResources().getDisplayMetrics().density;
 
         // Calculate the drawer header padding.  This is used to move the text in the drawer headers below any cutouts.
         drawerHeaderPaddingLeftAndRight = (int) (15 * screenDensity);
@@ -991,7 +958,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         // Initialize the user agent array adapter and string array.
         userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item);
-        userAgentDataArray = resources.getStringArray(R.array.user_agent_data);
+        userAgentDataArray = getResources().getStringArray(R.array.user_agent_data);
 
         // Get the intent that started the app.
         Intent launchingIntent = getIntent();
@@ -1085,7 +1052,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             sendBroadcast(orbotIntent);
         }
 
-        // Apply the app settings if returning from the Settings activity..
+        // Apply the app settings if returning from the Settings activity.
         if (reapplyAppSettingsOnRestart) {
             // Apply the app settings.
             applyAppSettings();
@@ -1105,8 +1072,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         // Get the nested scroll WebView from the tab fragment.
                         NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
 
-                        // TODO this doesn't seem to work if for WebViews that aren't visible.
-                        // Reload the WebView.
+                        // Reload the WebView.  This doesn't seem to work if for WebViews that aren't visible.
                         nestedScrollWebView.reload();
                     }
                 }
@@ -1119,10 +1085,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             reapplyAppSettingsOnRestart = false;
         }
 
+        // TODO apply to all the tabs.
         // Apply the domain settings if returning from the Domains activity.
         if (reapplyDomainSettingsOnRestart) {
             // Reapply the domain settings.
-            applyDomainSettings(formattedUrlString, false, true);
+            applyDomainSettings(currentWebView, formattedUrlString, false, true);
 
             // Reset `reapplyDomainSettingsOnRestart`.
             reapplyDomainSettingsOnRestart = false;
@@ -1588,6 +1555,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     reapplyDomainSettingsOnRestart = true;
                     currentDomainName = "";
 
+                    // TODO.  Move these to `putExtra`.  The certificate can be stored as strings.
+                    // Store the current SSL certificate and IP addresses in the domains activity.
+                    DomainsActivity.currentSslCertificate = currentWebView.getCertificate();
+                    DomainsActivity.currentIpAddresses = currentWebView.getCurrentIpAddresses();
+
                     // Create an intent to launch the domains activity.
                     Intent domainsIntent = new Intent(this, DomainsActivity.class);
 
@@ -1612,6 +1584,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // Create the domain and store the database ID.
                     int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain);
 
+                    // TODO.  Move these to `putExtra`.  The certificate can be stored as strings.
+                    // Store the current SSL certificate and IP addresses in the domains activity.
+                    DomainsActivity.currentSslCertificate = currentWebView.getCertificate();
+                    DomainsActivity.currentIpAddresses = currentWebView.getCurrentIpAddresses();
+
                     // Create an intent to launch the domains activity.
                     Intent domainsIntent = new Intent(this, DomainsActivity.class);
 
@@ -2296,9 +2273,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Clear the cache.
                 if (clearEverything || sharedPreferences.getBoolean("clear_cache", true)) {
-                    // Clear the cache.
-                    // TODO
-                    currentWebView.clearCache(true);
+                    // Clear the cache from each WebView.
+                    for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+                        // Get the WebView tab fragment.
+                        WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
+
+                        // Get the fragment view.
+                        View fragmentView = webViewTabFragment.getView();
+
+                        // Only clear the cache if the WebView exists.
+                        if (fragmentView != null) {
+                            // Get the nested scroll WebView from the tab fragment.
+                            NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+                            // Clear the cache for this WebView.
+                            nestedScrollWebView.clearCache(true);
+                        }
+                    }
 
                     // Manually delete the cache directories.
                     try {
@@ -2317,24 +2308,36 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     }
                 }
 
-                // Clear SSL certificate preferences.
-                // TODO
-                currentWebView.clearSslPreferences();
+                // Wipe out each WebView.
+                for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+                    // Get the WebView tab fragment.
+                    WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
 
-                // Clear the back/forward history.
-                // TODO
-                currentWebView.clearHistory();
+                    // Get the fragment view.
+                    View fragmentView = webViewTabFragment.getView();
+
+                    // Only wipe out the WebView if it exists.
+                    if (fragmentView != null) {
+                        // Get the nested scroll WebView from the tab fragment.
+                        NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+                        // Clear SSL certificate preferences for this WebView.
+                        nestedScrollWebView.clearSslPreferences();
 
-                // Clear `formattedUrlString`.
+                        // Clear the back/forward history for this WebView.
+                        nestedScrollWebView.clearHistory();
+
+                        // Destroy the internal state of `mainWebView`.
+                        nestedScrollWebView.destroy();
+                    }
+                }
+
+                // Clear the formatted URL string.
                 formattedUrlString = null;
 
-                // Clear `customHeaders`.
+                // Clear the custom headers.
                 customHeaders.clear();
 
-                // Destroy the internal state of `mainWebView`.
-                // TODO
-                currentWebView.destroy();
-
                 // Manually delete the `app_webview` folder, which contains the cookies, DOM storage, form data, and `Service Worker` cache.
                 // See `https://code.google.com/p/android/issues/detail?id=233826&thanks=233826&ts=1486670530`.
                 if (clearEverything) {
@@ -2425,6 +2428,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 reapplyDomainSettingsOnRestart = true;
                 currentDomainName = "";
 
+                // TODO.  Move these to `putExtra`.  The certificate can be stored as strings.
+                // Store the current SSL certificate and IP addresses in the domains activity.
+                DomainsActivity.currentSslCertificate = currentWebView.getCertificate();
+                DomainsActivity.currentIpAddresses = currentWebView.getCurrentIpAddresses();
+
                 // Launch the domains activity.
                 Intent domainsIntent = new Intent(this, DomainsActivity.class);
                 startActivity(domainsIntent);
@@ -3216,13 +3224,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     @Override
-    public void onPinnedMismatchBack() {
+    public void onPinnedMismatchBack() {  // TODO.  Move this logic to the dialog.
         if (currentWebView.canGoBack()) {  // There is a back page in the history.
             // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
-            formattedUrlString = "";
+            formattedUrlString = "";  // TODO.
 
             // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
-            navigatingHistory = true;
+            navigatingHistory = true;  // TODO.
 
             // Go back.
             currentWebView.goBack();
@@ -3233,9 +3241,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     @Override
-    public void onPinnedMismatchProceed() {
+    public void onPinnedMismatchProceed() {  // TODO.  Move this logic to the dialog.
         // Do not check the pinned information for this domain again until the domain changes.
-        ignorePinnedDomainInformation = true;
+        currentWebView.setIgnorePinnedDomainInformation(true);
     }
 
     @Override
@@ -3378,7 +3386,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         formattedUrlString = url;
 
         // Apply the domain settings.
-        applyDomainSettings(url, true, false);
+        applyDomainSettings(currentWebView, url, true, false);
 
         // If loading a website, set `urlIsLoading` to prevent changes in the user agent on websites with redirects from reloading the current website.
         urlIsLoading = !url.equals("");
@@ -3447,7 +3455,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             customHeaders.remove("DNT");
         }
 
-        // TODO this also needs to be set when creating a new tab.
         // Set the app bar scrolling for each WebView.
         for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
             // Get the WebView tab fragment.
@@ -3501,7 +3508,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Show the banner ad in the free flavor.
             if (BuildConfig.FLAVOR.contentEquals("free")) {
                 // Initialize the ads.  If this isn't the first run, `loadAd()` will be automatically called instead.
-                AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), fragmentManager, getString(R.string.google_app_id), getString(R.string.ad_unit_id));
+                AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getSupportFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id));
             }
 
             // Remove the `SYSTEM_UI` flags from the root frame layout.
@@ -3516,12 +3523,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `reloadWebsite` is used if returning from the Domains activity.  Otherwise JavaScript might not function correctly if it is newly enabled.
     // The deprecated `.getDrawable()` must be used until the minimum API >= 21.
     @SuppressWarnings("deprecation")
-    private boolean applyDomainSettings(String url, boolean resetFavoriteIcon, boolean reloadWebsite) {
+    private boolean applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetFavoriteIcon, boolean reloadWebsite) {
         // Get a handle for the URL edit text.
         EditText urlEditText = findViewById(R.id.url_edittext);
 
         // Get the current user agent.
-        String initialUserAgent = currentWebView.getSettings().getUserAgentString();
+        String initialUserAgent = nestedScrollWebView.getSettings().getUserAgentString();
 
         // Initialize a variable to track if the user agent changes.
         boolean userAgentChanged = false;
@@ -3538,10 +3545,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // If either `hostName` or `currentDomainName` are `null`, run the options for loading a new domain name.
         // The lint suggestion to simplify the `if` statement is incorrect, because `hostName.equals(currentDomainName)` can produce a `null object reference.`
         //noinspection SimplifiableIfStatement
-        if ((hostName == null) || (currentDomainName == null)) {
+        if ((hostName == null) || (currentDomainName == null)) {  // TODO.
             loadingNewDomainName = true;
         } else {  // Determine if `hostName` equals `currentDomainName`.
-            loadingNewDomainName = !hostName.equals(currentDomainName);
+            loadingNewDomainName = !hostName.equals(currentDomainName);  // TODO.
         }
 
         // Strings don't like to be null.
@@ -3552,21 +3559,25 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Only apply the domain settings if a new domain is being loaded.  This allows the user to set temporary settings for JavaScript, cookies, DOM storage, etc.
         if (loadingNewDomainName) {
             // Set the new `hostname` as the `currentDomainName`.
-            currentDomainName = hostName;
+            currentDomainName = hostName;  // TODO.
 
             // Reset the ignoring of pinned domain information.
-            ignorePinnedDomainInformation = false;
+            nestedScrollWebView.setIgnorePinnedDomainInformation(false);
+
+            // Clear any pinned SSL certificate or IP addresses.
+            nestedScrollWebView.clearPinnedSslCertificate();
+            nestedScrollWebView.clearPinnedIpAddresses();
 
             // Reset the favorite icon if specified.
             if (resetFavoriteIcon) {
                 // Store the favorite icon bitmap.
-                favoriteIconBitmap = favoriteIconDefaultBitmap;
+                favoriteIconBitmap = favoriteIconDefaultBitmap;  // TODO.
 
                 // Get a handle for the tab layout.
                 TabLayout tabLayout = findViewById(R.id.tablayout);
 
                 // Get the current tab.
-                TabLayout.Tab currentTab = tabLayout.getTabAt(tabLayout.getSelectedTabPosition());
+                TabLayout.Tab currentTab = tabLayout.getTabAt(tabLayout.getSelectedTabPosition());  // TODO.  We need to get the tab for this WebView, which might not be the current tab.
 
                 // Remove the warning below that the current tab might be null.
                 assert currentTab != null;
@@ -3620,17 +3631,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 domainNameInDatabase = hostName;
 
                 // Set the domain settings applied tracker to true.
-                currentWebView.setDomainSettingsApplied(true);
+                nestedScrollWebView.setDomainSettingsApplied(true);
             } else {  // The hostname is not contained in the domain settings set.
                 // Set the domain settings applied tracker to false.
-                currentWebView.setDomainSettingsApplied(false);
+                nestedScrollWebView.setDomainSettingsApplied(false);
             }
 
             // Check all the subdomains of the host name against wildcard domains in the domain cursor.
-            while (!currentWebView.getDomainSettingsApplied() && hostName.contains(".")) {  // Stop checking if domain settings are already applied or there are no more `.` in the host name.
+            while (!nestedScrollWebView.getDomainSettingsApplied() && hostName.contains(".")) {  // Stop checking if domain settings are already applied or there are no more `.` in the host name.
                 if (domainSettingsSet.contains("*." + hostName)) {  // Check the host name prepended by `*.`.
                     // Set the domain settings applied tracker to true.
-                    currentWebView.setDomainSettingsApplied(true);
+                    nestedScrollWebView.setDomainSettingsApplied(true);
 
                     // Store the applied domain names as it appears in the database.
                     domainNameInDatabase = "*." + hostName;
@@ -3647,131 +3658,147 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Store the general preference information.
             String defaultFontSizeString = sharedPreferences.getString("font_size", getString(R.string.font_size_default_value));
             String defaultUserAgentName = sharedPreferences.getString("user_agent", getString(R.string.user_agent_default_value));
-            defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value));
+            defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value));  // TODO.
             boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
-            nightMode = sharedPreferences.getBoolean("night_mode", false);
+            nightMode = sharedPreferences.getBoolean("night_mode", false);  // TODO.
             boolean displayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true);
 
-            if (currentWebView.getDomainSettingsApplied()) {  // The url has custom domain settings.
+            if (nestedScrollWebView.getDomainSettingsApplied()) {  // The url has custom domain settings.
                 // Get a cursor for the current host and move it to the first position.
                 Cursor currentHostDomainSettingsCursor = domainsDatabaseHelper.getCursorForDomainName(domainNameInDatabase);
                 currentHostDomainSettingsCursor.moveToFirst();
 
                 // Get the settings from the cursor.
-                currentWebView.setDomainSettingsDatabaseId(currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID)));
-                javaScriptEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
-                firstPartyCookiesEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1);
-                thirdPartyCookiesEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1);
-                domStorageEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
+                nestedScrollWebView.setDomainSettingsDatabaseId(currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID)));
+                javaScriptEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);  // TODO.
+                firstPartyCookiesEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1);  // TODO.
+                thirdPartyCookiesEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1);  // TODO.
+                domStorageEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);  // TODO.
                 // Form data can be removed once the minimum API >= 26.
-                saveFormDataEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1);
-                easyListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1);
-                easyPrivacyEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1);
-                fanboysAnnoyanceListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1);
-                fanboysSocialBlockingListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1);
-                ultraPrivacyEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)) == 1);
-                blockAllThirdPartyRequests = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1);
+                saveFormDataEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1);  // TODO.
+                easyListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1);  // TODO.
+                easyPrivacyEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1);  // TODO.
+                fanboysAnnoyanceListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1);  // TODO.
+                fanboysSocialBlockingListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1);  // TODO.
+                ultraPrivacyEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)) == 1);  // TODO.
+                blockAllThirdPartyRequests = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1);  // TODO.
                 String userAgentName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
                 int fontSize = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
                 int swipeToRefreshInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
                 int nightModeInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE));
                 int displayWebpageImagesInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
-                pinnedSslCertificate = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1);
-                pinnedSslIssuedToCName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME));
-                pinnedSslIssuedToOName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION));
-                pinnedSslIssuedToUName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT));
-                pinnedSslIssuedByCName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME));
-                pinnedSslIssuedByOName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION));
-                pinnedSslIssuedByUName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT));
-                pinnedIpAddresses = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)) == 1);
-                pinnedHostIpAddresses = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES));
+                boolean pinnedSslCertificate = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1);
+                String pinnedSslIssuedToCName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME));
+                String pinnedSslIssuedToOName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION));
+                String pinnedSslIssuedToUName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT));
+                String pinnedSslIssuedByCName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME));
+                String pinnedSslIssuedByOName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION));
+                String pinnedSslIssuedByUName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT));
+                boolean pinnedIpAddresses = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)) == 1);
+                String pinnedHostIpAddresses = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES));
+
+                // Create the pinned SSL date variables.
+                Date pinnedSslStartDate;
+                Date pinnedSslEndDate;
+
+                // Set the pinned SSL certificate start date to `null` if the saved date `long` is 0 because creating a new Date results in an error if the input is 0.
+                if (currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) == 0) {
+                    pinnedSslStartDate = null;
+                } else {
+                    pinnedSslStartDate = new Date(currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)));
+                }
+
+                // Set the pinned SSL certificate end date to `null` if the saved date `long` is 0 because creating a new Date results in an error if the input is 0.
+                if (currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) == 0) {
+                    pinnedSslEndDate = null;
+                } else {
+                    pinnedSslEndDate = new Date(currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)));
+                }
+
+                // If there is a pinned SSL certificate, store it in the WebView.
+                if (pinnedSslCertificate) {
+                    nestedScrollWebView.setPinnedSslCertificate(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName,
+                            pinnedSslStartDate, pinnedSslEndDate);
+                }
+
+                // If there is a pinned IP address, store it in the WebView.
+                if (pinnedIpAddresses) {
+                    nestedScrollWebView.setPinnedIpAddresses(pinnedHostIpAddresses);
+                }
 
                 // Set `nightMode` according to `nightModeInt`.  If `nightModeInt` is `DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT` the current setting from `sharedPreferences` will be used.
                 switch (nightModeInt) {
                     case DomainsDatabaseHelper.NIGHT_MODE_ENABLED:
-                        nightMode = true;
+                        nightMode = true;  // TODO.
                         break;
 
                     case DomainsDatabaseHelper.NIGHT_MODE_DISABLED:
-                        nightMode = false;
+                        nightMode = false;  // TODO.
                         break;
                 }
 
+                // TODO.
                 // Store the domain JavaScript status.  This is used by the options menu night mode toggle.
                 domainSettingsJavaScriptEnabled = javaScriptEnabled;
 
                 // Enable JavaScript if night mode is enabled.
                 if (nightMode) {
-                    javaScriptEnabled = true;
-                }
-
-                // Set the pinned SSL certificate start date to `null` if the saved date `long` is 0.
-                if (currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) == 0) {
-                    pinnedSslStartDate = null;
-                } else {
-                    pinnedSslStartDate = new Date(currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)));
-                }
-
-                // Set the pinned SSL certificate end date to `null` if the saved date `long` is 0.
-                if (currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) == 0) {
-                    pinnedSslEndDate = null;
-                } else {
-                    pinnedSslEndDate = new Date(currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)));
+                    javaScriptEnabled = true;  // TODO.
                 }
 
                 // Close `currentHostDomainSettingsCursor`.
                 currentHostDomainSettingsCursor.close();
 
                 // Apply the domain settings.
-                currentWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
-                cookieManager.setAcceptCookie(firstPartyCookiesEnabled);
-                currentWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
+                nestedScrollWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);  // TODO.
+                cookieManager.setAcceptCookie(firstPartyCookiesEnabled);  //TODO  This could be bad.
+                nestedScrollWebView.getSettings().setDomStorageEnabled(domStorageEnabled);  // TODO.
 
                 // Apply the form data setting if the API < 26.
                 if (Build.VERSION.SDK_INT < 26) {
-                    currentWebView.getSettings().setSaveFormData(saveFormDataEnabled);
+                    nestedScrollWebView.getSettings().setSaveFormData(saveFormDataEnabled);
                 }
 
                 // Apply the font size.
                 if (fontSize == 0) {  // Apply the default font size.
-                    currentWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
+                    nestedScrollWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
                 } else {  // Apply the specified font size.
-                    currentWebView.getSettings().setTextZoom(fontSize);
+                    nestedScrollWebView.getSettings().setTextZoom(fontSize);
                 }
 
                 // Set third-party cookies status if API >= 21.
                 if (Build.VERSION.SDK_INT >= 21) {
-                    cookieManager.setAcceptThirdPartyCookies(currentWebView, thirdPartyCookiesEnabled);
+                    cookieManager.setAcceptThirdPartyCookies(nestedScrollWebView, thirdPartyCookiesEnabled);  // TODO.
                 }
 
                 // Only set the user agent if the webpage is not currently loading.  Otherwise, changing the user agent on redirects can cause the original website to reload.
                 // <https://redmine.stoutner.com/issues/160>
-                if (!urlIsLoading) {
+                if (!urlIsLoading) {  // TODO.  We need to track this by WebView.
                     // Set the user agent.
                     if (userAgentName.equals(getString(R.string.system_default_user_agent))) {  // Use the system default user agent.
                         // Get the array position of the default user agent name.
-                        int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
+                        int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);  // TODO Could this be local.
 
                         // Set the user agent according to the system default.
                         switch (defaultUserAgentArrayPosition) {
                             case UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
                                 // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
-                                currentWebView.getSettings().setUserAgentString(defaultUserAgentName);
+                                nestedScrollWebView.getSettings().setUserAgentString(defaultUserAgentName);
                                 break;
 
                             case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
                                 // Set the user agent to `""`, which uses the default value.
-                                currentWebView.getSettings().setUserAgentString("");
+                                nestedScrollWebView.getSettings().setUserAgentString("");
                                 break;
 
                             case SETTINGS_CUSTOM_USER_AGENT:
                                 // Set the custom user agent.
-                                currentWebView.getSettings().setUserAgentString(defaultCustomUserAgentString);
+                                nestedScrollWebView.getSettings().setUserAgentString(defaultCustomUserAgentString);
                                 break;
 
                             default:
                                 // Get the user agent string from the user agent data array
-                                currentWebView.getSettings().setUserAgentString(userAgentDataArray[defaultUserAgentArrayPosition]);
+                                nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[defaultUserAgentArrayPosition]);
                         }
                     } else {  // Set the user agent according to the stored name.
                         // Get the array position of the user agent name.
@@ -3779,29 +3806,29 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                         switch (userAgentArrayPosition) {
                             case UNRECOGNIZED_USER_AGENT:  // The user agent name contains a custom user agent.
-                                currentWebView.getSettings().setUserAgentString(userAgentName);
+                                nestedScrollWebView.getSettings().setUserAgentString(userAgentName);
                                 break;
 
                             case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
                                 // Set the user agent to `""`, which uses the default value.
-                                currentWebView.getSettings().setUserAgentString("");
+                                nestedScrollWebView.getSettings().setUserAgentString("");
                                 break;
 
                             default:
                                 // Get the user agent string from the user agent data array.
-                                currentWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
+                                nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
                         }
                     }
 
                     // Store the applied user agent string, which is used in the View Source activity.
-                    appliedUserAgentString = currentWebView.getSettings().getUserAgentString();
+                    appliedUserAgentString = nestedScrollWebView.getSettings().getUserAgentString();  // TODO.
 
                     // Update the user agent change tracker.
-                    userAgentChanged = !appliedUserAgentString.equals(initialUserAgent);
+                    userAgentChanged = !appliedUserAgentString.equals(initialUserAgent);  // TODO.
                 }
 
                 // Set swipe to refresh.
-                switch (swipeToRefreshInt) {
+                switch (swipeToRefreshInt) {  // TODO.  This needs to be set and updated by tab.
                     case DomainsDatabaseHelper.SWIPE_TO_REFRESH_SYSTEM_DEFAULT:
                         // Set swipe to refresh according to the default.
                         swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
@@ -3820,15 +3847,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Set the loading of webpage images.
                 switch (displayWebpageImagesInt) {
                     case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_SYSTEM_DEFAULT:
-                        currentWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
+                        nestedScrollWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
                         break;
 
                     case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED:
-                        currentWebView.getSettings().setLoadsImagesAutomatically(true);
+                        nestedScrollWebView.getSettings().setLoadsImagesAutomatically(true);
                         break;
 
                     case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED:
-                        currentWebView.getSettings().setLoadsImagesAutomatically(false);
+                        nestedScrollWebView.getSettings().setLoadsImagesAutomatically(false);
                         break;
                 }
 
@@ -3840,28 +3867,28 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 }
             } else {  // The new URL does not have custom domain settings.  Load the defaults.
                 // Store the values from `sharedPreferences` in variables.
-                javaScriptEnabled = sharedPreferences.getBoolean("javascript", false);
-                firstPartyCookiesEnabled = sharedPreferences.getBoolean("first_party_cookies", false);
-                thirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false);
-                domStorageEnabled = sharedPreferences.getBoolean("dom_storage", false);
-                saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data", false);  // Form data can be removed once the minimum API >= 26.
-                easyListEnabled = sharedPreferences.getBoolean("easylist", true);
-                easyPrivacyEnabled = sharedPreferences.getBoolean("easyprivacy", true);
-                fanboysAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboys_annoyance_list", true);
-                fanboysSocialBlockingListEnabled = sharedPreferences.getBoolean("fanboys_social_blocking_list", true);
-                ultraPrivacyEnabled = sharedPreferences.getBoolean("ultraprivacy", true);
-                blockAllThirdPartyRequests = sharedPreferences.getBoolean("block_all_third_party_requests", false);
+                javaScriptEnabled = sharedPreferences.getBoolean("javascript", false);  // TODO.
+                firstPartyCookiesEnabled = sharedPreferences.getBoolean("first_party_cookies", false);  // TODO.
+                thirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false);  // TODO.
+                domStorageEnabled = sharedPreferences.getBoolean("dom_storage", false);  // TODO.
+                saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data", false);  // Form data can be removed once the minimum API >= 26.  // TODO.
+                easyListEnabled = sharedPreferences.getBoolean("easylist", true);  // TODO.
+                easyPrivacyEnabled = sharedPreferences.getBoolean("easyprivacy", true);  // TODO.
+                fanboysAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboys_annoyance_list", true);  // TODO.
+                fanboysSocialBlockingListEnabled = sharedPreferences.getBoolean("fanboys_social_blocking_list", true);  // TODO.
+                ultraPrivacyEnabled = sharedPreferences.getBoolean("ultraprivacy", true);  // TODO.
+                blockAllThirdPartyRequests = sharedPreferences.getBoolean("block_all_third_party_requests", false);  // TODO.
 
                 // Set `javaScriptEnabled` to be `true` if `night_mode` is `true`.
                 if (nightMode) {
-                    javaScriptEnabled = true;
+                    javaScriptEnabled = true;  // TODO.
                 }
 
                 // Apply the default settings.
-                currentWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
-                cookieManager.setAcceptCookie(firstPartyCookiesEnabled);
-                currentWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
-                currentWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
+                nestedScrollWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);  // TODO.
+                cookieManager.setAcceptCookie(firstPartyCookiesEnabled);  // TODO.
+                nestedScrollWebView.getSettings().setDomStorageEnabled(domStorageEnabled);  // TODO.
+                nestedScrollWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
                 swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
 
                 // Apply the form data setting if the API < 26.
@@ -3870,27 +3897,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 }
 
                 // Reset the pinned variables.
-                currentWebView.setDomainSettingsDatabaseId(-1);
-                pinnedSslCertificate = false;
-                pinnedSslIssuedToCName = "";
-                pinnedSslIssuedToOName = "";
-                pinnedSslIssuedToUName = "";
-                pinnedSslIssuedByCName = "";
-                pinnedSslIssuedByOName = "";
-                pinnedSslIssuedByUName = "";
-                pinnedSslStartDate = null;
-                pinnedSslEndDate = null;
-                pinnedIpAddresses = false;
-                pinnedHostIpAddresses = "";
+                nestedScrollWebView.setDomainSettingsDatabaseId(-1);
 
                 // Set third-party cookies status if API >= 21.
                 if (Build.VERSION.SDK_INT >= 21) {
-                    cookieManager.setAcceptThirdPartyCookies(currentWebView, thirdPartyCookiesEnabled);
+                    cookieManager.setAcceptThirdPartyCookies(nestedScrollWebView, thirdPartyCookiesEnabled);
                 }
 
                 // Only set the user agent if the webpage is not currently loading.  Otherwise, changing the user agent on redirects can cause the original website to reload.
                 // <https://redmine.stoutner.com/issues/160>
-                if (!urlIsLoading) {
+                if (!urlIsLoading) {  // TODO.
                     // Get the array position of the user agent name.
                     int userAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
 
@@ -3898,33 +3914,33 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     switch (userAgentArrayPosition) {
                         case UNRECOGNIZED_USER_AGENT:  // The default user agent name is not on the canonical list.
                             // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
-                            currentWebView.getSettings().setUserAgentString(defaultUserAgentName);
+                            nestedScrollWebView.getSettings().setUserAgentString(defaultUserAgentName);
                             break;
 
                         case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
                             // Set the user agent to `""`, which uses the default value.
-                            currentWebView.getSettings().setUserAgentString("");
+                            nestedScrollWebView.getSettings().setUserAgentString("");
                             break;
 
                         case SETTINGS_CUSTOM_USER_AGENT:
                             // Set the custom user agent.
-                            currentWebView.getSettings().setUserAgentString(defaultCustomUserAgentString);
+                            nestedScrollWebView.getSettings().setUserAgentString(defaultCustomUserAgentString);  // TODO.
                             break;
 
                         default:
                             // Get the user agent string from the user agent data array
-                            currentWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
+                            nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
                     }
 
                     // Store the applied user agent string, which is used in the View Source activity.
-                    appliedUserAgentString = currentWebView.getSettings().getUserAgentString();
+                    appliedUserAgentString = nestedScrollWebView.getSettings().getUserAgentString();  // TODO.
 
                     // Update the user agent change tracker.
-                    userAgentChanged = !appliedUserAgentString.equals(initialUserAgent);
+                    userAgentChanged = !appliedUserAgentString.equals(initialUserAgent);  // TODO.
                 }
 
                 // Set the loading of webpage images.
-                currentWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
+                nestedScrollWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
 
                 // Set a transparent background on URL edit text.  The deprecated `.getDrawable()` must be used until the minimum API >= 21.
                 urlEditText.setBackgroundDrawable(getResources().getDrawable(R.color.transparent));
@@ -3933,15 +3949,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Close the domains database helper.
             domainsDatabaseHelper.close();
 
-            // Update the privacy icons, but only if `mainMenu` has already been populated.
-            if (mainMenu != null) {
+            // Update the privacy icons, but only if the options menu has already been populated.
+            if (mainMenu != null) {  // TODO.  Consider renaming this to optionsMenu.
                 updatePrivacyIcons(true);
             }
         }
 
         // Reload the website if returning from the Domains activity.
         if (reloadWebsite) {
-            currentWebView.reload();
+            nestedScrollWebView.reload();
         }
 
         // Return the user agent changed status.
@@ -4273,70 +4289,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         startActivity(openWithBrowserIntent);
     }
 
-    public static void checkPinnedMismatch(int domainSettingsDatabaseId) {
-        if ((pinnedSslCertificate || pinnedIpAddresses) && !ignorePinnedDomainInformation) {
-            // Initialize the current SSL certificate variables.
-            String currentWebsiteIssuedToCName = "";
-            String currentWebsiteIssuedToOName = "";
-            String currentWebsiteIssuedToUName = "";
-            String currentWebsiteIssuedByCName = "";
-            String currentWebsiteIssuedByOName = "";
-            String currentWebsiteIssuedByUName = "";
-            Date currentWebsiteSslStartDate = null;
-            Date currentWebsiteSslEndDate = null;
-
-
-            // Extract the individual pieces of information from the current website SSL certificate if it is not null.
-            if (sslCertificate != null) {
-                currentWebsiteIssuedToCName = sslCertificate.getIssuedTo().getCName();
-                currentWebsiteIssuedToOName = sslCertificate.getIssuedTo().getOName();
-                currentWebsiteIssuedToUName = sslCertificate.getIssuedTo().getUName();
-                currentWebsiteIssuedByCName = sslCertificate.getIssuedBy().getCName();
-                currentWebsiteIssuedByOName = sslCertificate.getIssuedBy().getOName();
-                currentWebsiteIssuedByUName = sslCertificate.getIssuedBy().getUName();
-                currentWebsiteSslStartDate = sslCertificate.getValidNotBeforeDate();
-                currentWebsiteSslEndDate = sslCertificate.getValidNotAfterDate();
-            }
-
-            // Initialize string variables to store the SSL certificate dates.  Strings are needed to compare the values below, which doesn't work with `Dates` if they are `null`.
-            String currentWebsiteSslStartDateString = "";
-            String currentWebsiteSslEndDateString = "";
-            String pinnedSslStartDateString = "";
-            String pinnedSslEndDateString = "";
-
-            // Convert the `Dates` to `Strings` if they are not `null`.
-            if (currentWebsiteSslStartDate != null) {
-                currentWebsiteSslStartDateString = currentWebsiteSslStartDate.toString();
-            }
-
-            if (currentWebsiteSslEndDate != null) {
-                currentWebsiteSslEndDateString = currentWebsiteSslEndDate.toString();
-            }
-
-            if (pinnedSslStartDate != null) {
-                pinnedSslStartDateString = pinnedSslStartDate.toString();
-            }
-
-            if (pinnedSslEndDate != null) {
-                pinnedSslEndDateString = pinnedSslEndDate.toString();
-            }
-
-            // Check to see if the pinned information matches the current information.
-            if ((pinnedIpAddresses && !currentHostIpAddresses.equals(pinnedHostIpAddresses)) || (pinnedSslCertificate && (!currentWebsiteIssuedToCName.equals(pinnedSslIssuedToCName) ||
-                    !currentWebsiteIssuedToOName.equals(pinnedSslIssuedToOName) || !currentWebsiteIssuedToUName.equals(pinnedSslIssuedToUName) ||
-                    !currentWebsiteIssuedByCName.equals(pinnedSslIssuedByCName) || !currentWebsiteIssuedByOName.equals(pinnedSslIssuedByOName) ||
-                    !currentWebsiteIssuedByUName.equals(pinnedSslIssuedByUName) || !currentWebsiteSslStartDateString.equals(pinnedSslStartDateString) ||
-                    !currentWebsiteSslEndDateString.equals(pinnedSslEndDateString)))) {
-
-                // Get a handle for the pinned mismatch alert dialog.
-                DialogFragment pinnedMismatchDialogFragment = PinnedMismatchDialog.displayDialog(domainSettingsDatabaseId, pinnedSslCertificate, pinnedIpAddresses);
-
-                // Show the pinned mismatch alert dialog.
-                pinnedMismatchDialogFragment.show(fragmentManager, "Pinned Mismatch");
-            }
-        }
-    }
-
     public void addTab(View view) {
         // Get a handle for the tab layout.
         TabLayout tabLayout = findViewById(R.id.tablayout);
@@ -4366,7 +4318,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     @Override
-    public void initializeWebView(long pageId, int pageNumber, ProgressBar progressBar, NestedScrollWebView nestedScrollWebView) {
+    public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar) {
         // Get handles for the activity views.
         FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
         DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
@@ -4379,12 +4331,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Remove the incorrect lint warnings below that the some of the views might be null.
         assert actionBar != null;
 
+        // Get a handle for the activity
+        Activity activity = this;
+
         // Get a handle for the shared preferences.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
         // Get the relevant preferences.
         boolean downloadWithExternalApp = sharedPreferences.getBoolean("download_with_external_app", false);
 
+        // Set the app bar scrolling.
+        nestedScrollWebView.setNestedScrollingEnabled(sharedPreferences.getBoolean("scroll_app_bar", true));
+
         // Allow pinch to zoom.
         nestedScrollWebView.getSettings().setBuiltInZoomControls(true);
 
@@ -4500,22 +4458,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     downloadContentLength = contentLength;
 
                     // Show a dialog if the user has previously denied the permission.
-                    if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {  // Show a dialog explaining the request first.
+                    if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {  // Show a dialog explaining the request first.
                         // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_FILE.
                         DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_FILE);
 
                         // Show the download location permission alert dialog.  The permission will be requested when the the dialog is closed.
-                        downloadLocationPermissionDialogFragment.show(fragmentManager, getString(R.string.download_location));
+                        downloadLocationPermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.download_location));
                     } else {  // Show the permission request directly.
                         // Request the permission.  The download dialog will be launched by `onRequestPermissionResult()`.
-                        ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE);
+                        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE);
                     }
                 } else {  // The storage permission has already been granted.
                     // Get a handle for the download file alert dialog.
                     DialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength);
 
                     // Show the download file alert dialog.
-                    downloadFileDialogFragment.show(fragmentManager, getString(R.string.download));
+                    downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
                 }
             }
         });
@@ -4605,7 +4563,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     favoriteIconBitmap = icon;
 
                     // Get the current page position.
-                    int currentPosition = webViewPagerAdapter.getPositionForId(pageId);
+                    int currentPosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
 
                     // Get the current tab.
                     TabLayout.Tab tab = tabLayout.getTabAt(currentPosition);
@@ -4631,7 +4589,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             @Override
             public void onReceivedTitle(WebView view, String title) {
                 // Get the current page position.
-                int currentPosition = webViewPagerAdapter.getPositionForId(pageId);
+                int currentPosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
 
                 // Get the current tab.
                 TabLayout.Tab tab = tabLayout.getTabAt(currentPosition);
@@ -4780,7 +4738,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     formattedUrlString = "";
 
                     // Apply the domain settings for the new URL.  `applyDomainSettings` doesn't do anything if the domain has not changed.
-                    boolean userAgentChanged = applyDomainSettings(url, true, false);
+                    boolean userAgentChanged = applyDomainSettings(nestedScrollWebView, url, true, false);
 
                     // Check if the user agent has changed.
                     if (userAgentChanged) {
@@ -4897,7 +4855,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 }
 
                 // Get the current WebView page position.
-                int webViewPagePosition = webViewPagerAdapter.getPositionForId(pageId);
+                int webViewPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
 
                 // Determine if the WebView is currently displayed.
                 boolean webViewDisplayed = (webViewPagePosition == tabLayout.getSelectedTabPosition());
@@ -5120,19 +5078,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Display the HTTP authentication dialog.
                 DialogFragment httpAuthenticationDialogFragment = HttpAuthenticationDialog.displayDialog(host, realm);
-                httpAuthenticationDialogFragment.show(fragmentManager, getString(R.string.http_authentication));
+                httpAuthenticationDialogFragment.show(getSupportFragmentManager(), getString(R.string.http_authentication));
             }
 
-            // Update the URL in urlTextBox when the page starts to load.
             @Override
             public void onPageStarted(WebView view, String url, Bitmap favicon) {
                 // Set `urlIsLoading` to `true`, so that redirects while loading do not trigger changes in the user agent, which forces another reload of the existing page.
                 // This is also used to determine when to check for pinned mismatches.
                 urlIsLoading = true;
 
-                // Reset the list of host IP addresses.
-                currentHostIpAddresses = "";
-
                 // Reset the list of resource requests.
                 nestedScrollWebView.clearResourceRequests();
 
@@ -5167,13 +5121,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // Get a URI for the current URL.
                     Uri currentUri = Uri.parse(formattedUrlString);
 
+                    // Reset the list of host IP addresses.
+                    nestedScrollWebView.clearCurrentIpAddresses();
+
                     // Get the IP addresses for the host.
-                    new GetHostIpAddresses(activity, currentWebView.getDomainSettingsDatabaseId()).execute(currentUri.getHost());
+                    new GetHostIpAddresses(activity, getSupportFragmentManager(), nestedScrollWebView).execute(currentUri.getHost());
 
                     // Apply any custom domain settings if the URL was loaded by navigating history.
                     if (navigatingHistory) {
                         // Apply the domain settings.
-                        boolean userAgentChanged = applyDomainSettings(url, true, false);
+                        boolean userAgentChanged = applyDomainSettings(nestedScrollWebView, url, true, false);
 
                         // Reset `navigatingHistory`.
                         navigatingHistory = false;
@@ -5268,7 +5225,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         inputMethodManager.showSoftInput(urlEditText, 0);
 
                         // Apply the domain settings.  This clears any settings from the previous domain.
-                        applyDomainSettings(formattedUrlString, true, false);
+                        applyDomainSettings(nestedScrollWebView, formattedUrlString, true, false);
                     } else {  // `WebView` has loaded a webpage.
                         // Set the formatted URL string.  Getting the URL from the WebView instead of using the one provided by `onPageFinished` makes websites like YouTube function correctly.
                         formattedUrlString = nestedScrollWebView.getUrl();
@@ -5283,12 +5240,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         }
                     }
 
-                    // Store the SSL certificate so it can be accessed from `ViewSslCertificateDialog` and `PinnedMismatchDialog`.
-                    sslCertificate = nestedScrollWebView.getCertificate();
-
                     // Check the current website information against any pinned domain information if the current IP addresses have been loaded.
-                    if (!gettingIpAddresses) {
-                        checkPinnedMismatch(currentWebView.getDomainSettingsDatabaseId());
+                    if ((nestedScrollWebView.hasPinnedSslCertificate() || nestedScrollWebView.hasPinnedIpAddresses()) && nestedScrollWebView.hasCurrentIpAddresses() &&
+                            !nestedScrollWebView.ignorePinnedDomainInformation()) {
+                        CheckPinnedMismatchHelper.checkPinnedMismatch(getSupportFragmentManager(), nestedScrollWebView);
                     }
                 }
 
@@ -5313,21 +5268,30 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 Date currentWebsiteSslEndDate = currentWebsiteSslCertificate.getValidNotAfterDate();
 
                 // Proceed to the website if the current SSL website certificate matches the pinned domain certificate.
-                if (pinnedSslCertificate &&
-                        currentWebsiteIssuedToCName.equals(pinnedSslIssuedToCName) && currentWebsiteIssuedToOName.equals(pinnedSslIssuedToOName) &&
-                        currentWebsiteIssuedToUName.equals(pinnedSslIssuedToUName) && currentWebsiteIssuedByCName.equals(pinnedSslIssuedByCName) &&
-                        currentWebsiteIssuedByOName.equals(pinnedSslIssuedByOName) && currentWebsiteIssuedByUName.equals(pinnedSslIssuedByUName) &&
-                        currentWebsiteSslStartDate.equals(pinnedSslStartDate) && currentWebsiteSslEndDate.equals(pinnedSslEndDate)) {
-
-                    // An SSL certificate is pinned and matches the current domain certificate.  Proceed to the website without displaying an error.
-                    handler.proceed();
+                if (nestedScrollWebView.hasPinnedSslCertificate()) {
+                    // Get the pinned SSL certificate.
+                    ArrayList<Object> pinnedSslCertificateArrayList = nestedScrollWebView.getPinnedSslCertificate();
+
+                    // Extract the arrays from the array list.
+                    String[] pinnedSslCertificateStringArray = (String[]) pinnedSslCertificateArrayList.get(0);
+                    Date[] pinnedSslCertificateDateArray = (Date[]) pinnedSslCertificateArrayList.get(1);
+
+                    // Check if the current SSL certificate matches the pinned certificate.
+                    if (currentWebsiteIssuedToCName.equals(pinnedSslCertificateStringArray[0]) && currentWebsiteIssuedToOName.equals(pinnedSslCertificateStringArray[1]) &&
+                        currentWebsiteIssuedToUName.equals(pinnedSslCertificateStringArray[2]) && currentWebsiteIssuedByCName.equals(pinnedSslCertificateStringArray[3]) &&
+                        currentWebsiteIssuedByOName.equals(pinnedSslCertificateStringArray[4]) && currentWebsiteIssuedByUName.equals(pinnedSslCertificateStringArray[5]) &&
+                        currentWebsiteSslStartDate.equals(pinnedSslCertificateDateArray[0]) && currentWebsiteSslEndDate.equals(pinnedSslCertificateDateArray[1])) {
+
+                        // An SSL certificate is pinned and matches the current domain certificate.  Proceed to the website without displaying an error.
+                        handler.proceed();
+                    }
                 } else {  // Either there isn't a pinned SSL certificate or it doesn't match the current website certificate.
                     // Store `handler` so it can be accesses from `onSslErrorCancel()` and `onSslErrorProceed()`.
-                    sslErrorHandler = handler;
+                    sslErrorHandler = handler;  // TODO.  We need to pass this in instead of using a static variable.  Because multiple could be displayed at once from different tabs.
 
                     // Display the SSL error `AlertDialog`.
                     DialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error);
-                    sslCertificateErrorDialogFragment.show(fragmentManager, getString(R.string.ssl_certificate_error));
+                    sslCertificateErrorDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_error));
                 }
             }
         });
index 3bec00521955b29d8e5ea315d00eb0b6242d7239..d7ea9cf072984bc83b71e041d63d3f762b5ea98c 100644 (file)
@@ -66,10 +66,10 @@ public class WebViewPagerAdapter extends FragmentPagerAdapter {
     @Override
     public long getItemId(int position) {
         // Return the unique ID for this page.
-        return webViewFragmentsList.get(position).tabId;
+        return webViewFragmentsList.get(position).fragmentId;
     }
 
-    public int getPositionForId(long pageId) {
+    public int getPositionForId(long fragmentId) {
         // Initialize the position variable.
         int position = -1;
 
@@ -79,7 +79,7 @@ public class WebViewPagerAdapter extends FragmentPagerAdapter {
         // Find the current position of the WebView fragment with the given ID.
         while (position < 0 && i < webViewFragmentsList.size()) {
             // Check to see if the tab ID of this WebView matches the page ID.
-            if (webViewFragmentsList.get(i).tabId == pageId) {
+            if (webViewFragmentsList.get(i).fragmentId == fragmentId) {
                 // Store the position if they are a match.
                 position = i;
             }
index 1e004d13ba4dc4dbca8f04971686c14d623503a1..3c7f37cfbb65bf0086e7a017d3a9a9fe9ee01011 100644 (file)
@@ -22,7 +22,11 @@ package com.stoutner.privacybrowser.asynctasks;
 import android.app.Activity;
 import android.os.AsyncTask;
 
+import androidx.fragment.app.FragmentManager;
+
 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
+import com.stoutner.privacybrowser.helpers.CheckPinnedMismatchHelper;
+import com.stoutner.privacybrowser.views.NestedScrollWebView;
 
 import java.lang.ref.WeakReference;
 import java.net.InetAddress;
@@ -32,39 +36,25 @@ import java.net.UnknownHostException;
 public class GetHostIpAddresses extends AsyncTask<String, Void, String> {
     // The weak references are used to determine if the activity have disappeared while the AsyncTask is running.
     private final WeakReference<Activity> activityWeakReference;
-    private final int domainSettingsDatabaseId;
+    private final WeakReference<FragmentManager> fragmentManagerWeakReference;
+    private final WeakReference<NestedScrollWebView> nestedScrollWebViewWeakReference;
 
-    public GetHostIpAddresses(Activity activity, int domainSettingsDatabaseId) {
-        // Populate the weak activity reference.
+    public GetHostIpAddresses(Activity activity, FragmentManager fragmentManager, NestedScrollWebView nestedScrollWebView) {
+        // Populate the weak references.
         activityWeakReference = new WeakReference<>(activity);
-
-        // Populate the domain settings database ID.
-        this.domainSettingsDatabaseId = domainSettingsDatabaseId;
-    }
-
-    // `onPreExecute()` operates on the UI thread.
-    @Override
-    protected void onPreExecute() {
-        // Get a handle for the activity.
-        Activity activity = activityWeakReference.get();
-
-        // Abort if the activity is gone.
-        if ((activity == null) || activity.isFinishing()) {
-            return;
-        }
-
-        // Set the getting IP addresses tracker.
-        MainWebViewActivity.gettingIpAddresses = true;
+        fragmentManagerWeakReference = new WeakReference<>(fragmentManager);
+        nestedScrollWebViewWeakReference = new WeakReference<>(nestedScrollWebView);
     }
 
-
     @Override
     protected String doInBackground(String... domainName) {
-        // Get a handle for the activity.
+        // Get a handles for the weak references.
         Activity activity = activityWeakReference.get();
+        FragmentManager fragmentManager = fragmentManagerWeakReference.get();
+        NestedScrollWebView nestedScrollWebView = nestedScrollWebViewWeakReference.get();
 
-        // Abort if the activity is gone.
-        if ((activity == null) || activity.isFinishing()) {
+        // Abort if the activity or its components are gone.
+        if ((activity == null) || activity.isFinishing() || fragmentManager == null || nestedScrollWebView == null) {
             // Return an empty spannable string builder.
             return "";
         }
@@ -101,22 +91,22 @@ public class GetHostIpAddresses extends AsyncTask<String, Void, String> {
     // `onPostExecute()` operates on the UI thread.
     @Override
     protected void onPostExecute(String ipAddresses) {
-        // Get a handle for the activity.
+        // Get a handle for the activity and the nested scroll WebView.
         Activity activity = activityWeakReference.get();
+        FragmentManager fragmentManager = fragmentManagerWeakReference.get();
+        NestedScrollWebView nestedScrollWebView = nestedScrollWebViewWeakReference.get();
 
-        // Abort if the activity is gone.
-        if ((activity == null) || activity.isFinishing()) {
+        // Abort if the activity or its components are gone.
+        if ((activity == null) || activity.isFinishing() || fragmentManager == null || nestedScrollWebView == null) {
             return;
         }
 
         // Store the IP addresses.
-        MainWebViewActivity.currentHostIpAddresses = ipAddresses;
+        nestedScrollWebView.setCurrentIpAddresses(ipAddresses);
 
-        if (!MainWebViewActivity.urlIsLoading) {
-            MainWebViewActivity.checkPinnedMismatch(domainSettingsDatabaseId);
+        //TODO.  Move `urlIsLoading` to the WebView.
+        if (!MainWebViewActivity.urlIsLoading && !nestedScrollWebView.ignorePinnedDomainInformation() && (nestedScrollWebView.hasPinnedSslCertificate() || nestedScrollWebView.hasPinnedIpAddresses())) {
+            CheckPinnedMismatchHelper.checkPinnedMismatch(fragmentManager, nestedScrollWebView);
         }
-
-        // Reset the getting IP addresses tracker.
-        MainWebViewActivity.gettingIpAddresses = false;
     }
 }
\ No newline at end of file
index 95a3ef5ed2bc7312cf585996d93bcc0dc4043692..b82f4bbf690ee8b46f5653ec202a4db970a41e55 100644 (file)
@@ -75,19 +75,19 @@ public class CreateHomeScreenShortcutDialog extends DialogFragment {
         // Convert the byte array output stream to a byte array.
         byte[] favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray();
 
-        // Create a bundle.
-        Bundle bundle = new Bundle();
+        // Create an arguments bundle.
+        Bundle argumentsBundle = new Bundle();
 
         // Store the variables in the bundle.
-        bundle.putString("shortcut_name", shortcutName);
-        bundle.putString("url_string", urlString);
-        bundle.putByteArray("favorite_icon_byte_array", favoriteIconByteArray);
+        argumentsBundle.putString("shortcut_name", shortcutName);
+        argumentsBundle.putString("url_string", urlString);
+        argumentsBundle.putByteArray("favorite_icon_byte_array", favoriteIconByteArray);
 
         // Create a new instance of the dialog.
         CreateHomeScreenShortcutDialog createHomeScreenShortcutDialog = new CreateHomeScreenShortcutDialog();
 
         // Add the bundle to the dialog.
-        createHomeScreenShortcutDialog.setArguments(bundle);
+        createHomeScreenShortcutDialog.setArguments(argumentsBundle);
 
         // Return the new dialog.
         return createHomeScreenShortcutDialog;
index 7e86bbdb740589a78c182ab1c0c2710313b6e013..c8a0a78844ebe4e9427fe3641a15f82171cce8f5 100644 (file)
@@ -32,7 +32,6 @@ import android.os.Bundle;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.style.ForegroundColorSpan;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
@@ -42,10 +41,13 @@ import com.google.android.material.tabs.TabLayout;
 
 import com.stoutner.privacybrowser.R;
 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
+import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
+import com.stoutner.privacybrowser.views.NestedScrollWebView;
 import com.stoutner.privacybrowser.views.WrapVerticalContentViewPager;
 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
 
 import java.text.DateFormat;
+import java.util.ArrayList;
 import java.util.Date;
 
 import androidx.annotation.NonNull;
@@ -53,9 +55,9 @@ import androidx.fragment.app.DialogFragment;  // The AndroidX dialog fragment mu
 import androidx.viewpager.widget.PagerAdapter;
 
 public class PinnedMismatchDialog extends DialogFragment {
-    // Instantiate the class variables.
+    // Declare the class variables.
     private PinnedMismatchListener pinnedMismatchListener;
-    private LayoutInflater layoutInflater;
+    private NestedScrollWebView nestedScrollWebView;
     private String currentSslIssuedToCName;
     private String currentSslIssuedToOName;
     private String currentSslIssuedToUName;
@@ -64,8 +66,6 @@ public class PinnedMismatchDialog extends DialogFragment {
     private String currentSslIssuedByUName;
     private Date currentSslStartDate;
     private Date currentSslEndDate;
-    private boolean pinnedSslCertificate;
-    private boolean pinnedIpAddresses;
 
     // The public interface is used to send information back to the parent activity.
     public interface PinnedMismatchListener {
@@ -83,19 +83,21 @@ public class PinnedMismatchDialog extends DialogFragment {
         pinnedMismatchListener = (PinnedMismatchListener) context;
     }
 
-    public static PinnedMismatchDialog displayDialog(int domainSettingsDatabaseId, boolean pinnedSslCertificate, boolean pinnedIpAddresses) {
+    public static PinnedMismatchDialog displayDialog(long webViewFragmentId) {
         // Create an arguments bundle.
         Bundle argumentsBundle = new Bundle();
 
-        // Store the variables in the bundle.
-        argumentsBundle.putInt("domain_settings_database_id", domainSettingsDatabaseId);
-        argumentsBundle.putBoolean("pinned_sss_certificate", pinnedSslCertificate);
-        argumentsBundle.putBoolean("pinned_ip_addresses", pinnedIpAddresses);
+        // Store the WebView position in the bundle.
+        argumentsBundle.putLong("webview_fragment_id", webViewFragmentId);
 
-        // Add the arguments bundle to this instance of `PinnedMismatchDialog`.
-        PinnedMismatchDialog thisPinnedMismatchDialog = new PinnedMismatchDialog();
-        thisPinnedMismatchDialog.setArguments(argumentsBundle);
-        return thisPinnedMismatchDialog;
+        // Create a new instance of the pinned mismatch dialog.
+        PinnedMismatchDialog pinnedMismatchDialog = new PinnedMismatchDialog();
+
+        // Add the arguments bundle to the new instance.
+        pinnedMismatchDialog.setArguments(argumentsBundle);
+
+        // Make it so.
+        return pinnedMismatchDialog;
     }
 
     // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
@@ -103,11 +105,23 @@ public class PinnedMismatchDialog extends DialogFragment {
     @Override
     @NonNull
     public Dialog onCreateDialog(Bundle savedInstanceState) {
-        // Remove the incorrect lint warning that `getActivity()` might be null.
-        assert getActivity() != null;
+        // Remove the incorrect lint warning below that `.getArguments().getInt()` might be null.
+        assert getArguments() != null;
+
+        // Get the current position of this WebView fragment.
+        int webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(getArguments().getLong("webview_fragment_id"));
+
+        // Get the WebView tab fragment.
+        WebViewTabFragment webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition);
+
+        // Get the fragment view.
+        View fragmentView = webViewTabFragment.getView();
+
+        // Remove the incorrect lint warning below that the fragment view might be null.
+        assert fragmentView != null;
 
-        // Get the activity's layout inflater.
-        layoutInflater = getActivity().getLayoutInflater();
+        // Get a handle for the current WebView.
+        nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
 
         // Use an alert dialog builder to create the alert dialog.
         AlertDialog.Builder dialogBuilder;
@@ -121,14 +135,6 @@ public class PinnedMismatchDialog extends DialogFragment {
             dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogLight);
         }
 
-        // Remove the incorrect lint warning below that `.getArguments.getBoolean()` might be null.
-        assert getArguments() != null;
-
-        // Get the variables from the bundle.
-        int domainSettingsDatabaseId = getArguments().getInt("domain_settings_database_id");
-        pinnedSslCertificate = getArguments().getBoolean("pinned_ssl_certificate");
-        pinnedIpAddresses = getArguments().getBoolean("pinned_ip_addresses");
-
         // Set the favorite icon as the dialog icon if it exists.
         if (MainWebViewActivity.favoriteIconBitmap.equals(MainWebViewActivity.favoriteIconDefaultBitmap)) {  // There is no favorite icon.
             // Set the icon according to the theme.
@@ -164,29 +170,23 @@ public class PinnedMismatchDialog extends DialogFragment {
             DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(getContext(), null, null, 0);
 
             // Update the SSL certificate if it is pinned.
-            if (pinnedSslCertificate) {
+            if (nestedScrollWebView.hasPinnedSslCertificate()) {
                 // Update the pinned SSL certificate in the domain database.
-                domainsDatabaseHelper.updatePinnedSslCertificate(domainSettingsDatabaseId, currentSslIssuedToCName, currentSslIssuedToOName, currentSslIssuedToUName,
+                domainsDatabaseHelper.updatePinnedSslCertificate(nestedScrollWebView.getDomainSettingsDatabaseId(), currentSslIssuedToCName, currentSslIssuedToOName, currentSslIssuedToUName,
                         currentSslIssuedByCName, currentSslIssuedByOName, currentSslIssuedByUName, currentSslStartDateLong, currentSslEndDateLong);
 
-                // Update the pinned SSL certificate class variables to match the information that is now in the database.
-                MainWebViewActivity.pinnedSslIssuedToCName = currentSslIssuedToCName;
-                MainWebViewActivity.pinnedSslIssuedToOName = currentSslIssuedToOName;
-                MainWebViewActivity.pinnedSslIssuedToUName = currentSslIssuedToUName;
-                MainWebViewActivity.pinnedSslIssuedByCName = currentSslIssuedByCName;
-                MainWebViewActivity.pinnedSslIssuedByOName = currentSslIssuedByOName;
-                MainWebViewActivity.pinnedSslIssuedByUName = currentSslIssuedByUName;
-                MainWebViewActivity.pinnedSslStartDate = currentSslStartDate;
-                MainWebViewActivity.pinnedSslEndDate = currentSslEndDate;
+                // Update the pinned SSL certificate in the nested scroll WebView.
+                nestedScrollWebView.setPinnedSslCertificate(currentSslIssuedToCName, currentSslIssuedToOName, currentSslIssuedToUName, currentSslIssuedByCName, currentSslIssuedByOName, currentSslIssuedByUName,
+                        currentSslStartDate, currentSslEndDate);
             }
 
             // Update the IP addresses if they are pinned.
-            if (pinnedIpAddresses) {
+            if (nestedScrollWebView.hasPinnedIpAddresses()) {
                 // Update the pinned IP addresses in the domain database.
-                domainsDatabaseHelper.updatePinnedIpAddresses(domainSettingsDatabaseId, MainWebViewActivity.currentHostIpAddresses);
+                domainsDatabaseHelper.updatePinnedIpAddresses(nestedScrollWebView.getDomainSettingsDatabaseId(), nestedScrollWebView.getCurrentIpAddresses());
 
-                // Update the pinned IP addresses class variable to match the information that is now in the database.
-                MainWebViewActivity.pinnedHostIpAddresses = MainWebViewActivity.currentHostIpAddresses;
+                // Update the pinned IP addresses in the nested scroll WebView.
+                nestedScrollWebView.setPinnedIpAddresses(nestedScrollWebView.getCurrentIpAddresses());
             }
         });
 
@@ -205,8 +205,12 @@ public class PinnedMismatchDialog extends DialogFragment {
         // Set the title.
         dialogBuilder.setTitle(R.string.pinned_mismatch);
 
+        // Remove the incorrect lint warning below that `getLayoutInflater()` might be null.
+        assert getActivity() != null;
+
         // Set the layout.  The parent view is `null` because it will be assigned by `AlertDialog`.
-        dialogBuilder.setView(layoutInflater.inflate(R.layout.pinned_mismatch_linearlayout, null));
+        // For some reason, `getLayoutInflater()` without `getActivity()` produces an endless loop (probably a bug that will be fixed at some point in the future).
+        dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.pinned_mismatch_linearlayout, null));
 
         // Create an alert dialog from the alert dialog builder.
         final AlertDialog alertDialog = dialogBuilder.create();
@@ -262,7 +266,7 @@ public class PinnedMismatchDialog extends DialogFragment {
         @NonNull
         public Object instantiateItem(@NonNull ViewGroup container, int position) {
             // Inflate the scroll view for this tab.
-            ViewGroup tabViewGroup = (ViewGroup) layoutInflater.inflate(R.layout.pinned_mismatch_scrollview, container, false);
+            ViewGroup tabViewGroup = (ViewGroup) getLayoutInflater().inflate(R.layout.pinned_mismatch_scrollview, container, false);
 
             // Get handles for the `TextViews`.
             TextView domainNameTextView = tabViewGroup.findViewById(R.id.domain_name);
@@ -292,7 +296,7 @@ public class PinnedMismatchDialog extends DialogFragment {
             String domainName = currentUri.getHost();
 
             // Get the current website SSL certificate.
-            SslCertificate sslCertificate = MainWebViewActivity.sslCertificate;
+            SslCertificate sslCertificate = nestedScrollWebView.getCertificate();
 
             // Extract the individual pieces of information from the current website SSL certificate if it is not null.
             if (sslCertificate != null) {
@@ -314,6 +318,13 @@ public class PinnedMismatchDialog extends DialogFragment {
                 currentSslIssuedByUName = "";
             }
 
+            // Get the pinned SSL certificate.
+            ArrayList<Object> pinnedSslCertificateArrayList = nestedScrollWebView.getPinnedSslCertificate();
+
+            // Extract the arrays from the array list.
+            String[] pinnedSslCertificateStringArray = (String[]) pinnedSslCertificateArrayList.get(0);
+            Date[] pinnedSslCertificateDateArray = (Date[]) pinnedSslCertificateArrayList.get(1);
+
             // Setup the domain name spannable string builder.
             SpannableStringBuilder domainNameStringBuilder = new SpannableStringBuilder(domainNameLabel + domainName);
 
@@ -331,7 +342,7 @@ public class PinnedMismatchDialog extends DialogFragment {
             // Setup the spannable string builders for each tab.
             if (position == 0) {  // Setup the current settings tab.
                 // Create the string builders.
-                ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + MainWebViewActivity.currentHostIpAddresses);
+                ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + nestedScrollWebView.getCurrentIpAddresses());
                 issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentSslIssuedToCName);
                 issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentSslIssuedToOName);
                 issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentSslIssuedToUName);
@@ -353,26 +364,25 @@ public class PinnedMismatchDialog extends DialogFragment {
                 }
             } else {  // Setup the pinned settings tab.
                 // Create the string builders.
-                ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + MainWebViewActivity.pinnedHostIpAddresses);
-                issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + MainWebViewActivity.pinnedSslIssuedToCName);
-                issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + MainWebViewActivity.pinnedSslIssuedToOName);
-                issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + MainWebViewActivity.pinnedSslIssuedToUName);
-                issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + MainWebViewActivity.pinnedSslIssuedByCName);
-                issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + MainWebViewActivity.pinnedSslIssuedByOName);
-                issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + MainWebViewActivity.pinnedSslIssuedByUName);
+                ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + nestedScrollWebView.getPinnedIpAddresses());
+                issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + pinnedSslCertificateStringArray[0]);
+                issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + pinnedSslCertificateStringArray[1]);
+                issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + pinnedSslCertificateStringArray[2]);
+                issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + pinnedSslCertificateStringArray[3]);
+                issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + pinnedSslCertificateStringArray[4]);
+                issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + pinnedSslCertificateStringArray[5]);
 
                 // Set the dates if they aren't `null`.
-                if (MainWebViewActivity.pinnedSslStartDate == null) {
+                if (pinnedSslCertificateDateArray[0] == null) {
                     startDateStringBuilder = new SpannableStringBuilder(startDateLabel);
                 } else {
-                    startDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
-                            .format(MainWebViewActivity.pinnedSslStartDate));
+                    startDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[0]));
                 }
 
-                if (MainWebViewActivity.pinnedSslEndDate == null) {
+                if (pinnedSslCertificateDateArray[1] == null) {
                     endDateStringBuilder = new SpannableStringBuilder(endDateLabel);
                 } else {
-                    endDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(MainWebViewActivity.pinnedSslEndDate));
+                    endDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[1]));
                 }
             }
 
@@ -395,8 +405,8 @@ public class PinnedMismatchDialog extends DialogFragment {
             domainNameStringBuilder.setSpan(blueColorSpan, domainNameLabel.length(), domainNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
 
             // Color coordinate the IP addresses if they are pinned.
-            if (pinnedIpAddresses) {
-                if (MainWebViewActivity.currentHostIpAddresses.equals(MainWebViewActivity.pinnedHostIpAddresses)) {
+            if (nestedScrollWebView.hasPinnedIpAddresses()) {
+                if (nestedScrollWebView.getCurrentIpAddresses().equals(nestedScrollWebView.getPinnedIpAddresses())) {
                     ipAddressesStringBuilder.setSpan(blueColorSpan, ipAddressesLabel.length(), ipAddressesStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 } else {
                     ipAddressesStringBuilder.setSpan(redColorSpan, ipAddressesLabel.length(), ipAddressesStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
@@ -404,50 +414,50 @@ public class PinnedMismatchDialog extends DialogFragment {
             }
 
             // Color coordinate the SSL certificate fields if they are pinned.
-            if (pinnedSslCertificate) {
-                if (currentSslIssuedToCName.equals(MainWebViewActivity.pinnedSslIssuedToCName)) {
+            if (nestedScrollWebView.hasPinnedSslCertificate()) {
+                if (currentSslIssuedToCName.equals(pinnedSslCertificateStringArray[0])) {
                     issuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 } else {
                     issuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 }
 
-                if (currentSslIssuedToOName.equals(MainWebViewActivity.pinnedSslIssuedToOName)) {
+                if (currentSslIssuedToOName.equals(pinnedSslCertificateStringArray[1])) {
                     issuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 } else {
                     issuedToONameStringBuilder.setSpan(redColorSpan, oNameLabel.length(), issuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 }
 
-                if (currentSslIssuedToUName.equals(MainWebViewActivity.pinnedSslIssuedToUName)) {
+                if (currentSslIssuedToUName.equals(pinnedSslCertificateStringArray[2])) {
                     issuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 } else {
                     issuedToUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length(), issuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 }
 
-                if (currentSslIssuedByCName.equals(MainWebViewActivity.pinnedSslIssuedByCName)) {
+                if (currentSslIssuedByCName.equals(pinnedSslCertificateStringArray[3])) {
                     issuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 } else {
                     issuedByCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 }
 
-                if (currentSslIssuedByOName.equals(MainWebViewActivity.pinnedSslIssuedByOName)) {
+                if (currentSslIssuedByOName.equals(pinnedSslCertificateStringArray[4])) {
                     issuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 } else {
                     issuedByONameStringBuilder.setSpan(redColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 }
 
-                if (currentSslIssuedByUName.equals(MainWebViewActivity.pinnedSslIssuedByUName)) {
+                if (currentSslIssuedByUName.equals(pinnedSslCertificateStringArray[5])) {
                     issuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 } else {
                     issuedByUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 }
 
-                if ((currentSslStartDate != null) && currentSslStartDate.equals(MainWebViewActivity.pinnedSslStartDate)) {
+                if ((currentSslStartDate != null) && currentSslStartDate.equals(pinnedSslCertificateDateArray[0])) {
                     startDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 } else {
                     startDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 }
 
-                if ((currentSslEndDate != null) && currentSslEndDate.equals(MainWebViewActivity.pinnedSslEndDate)) {
+                if ((currentSslEndDate != null) && currentSslEndDate.equals(pinnedSslCertificateDateArray[1])) {
                     endDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 } else {
                     endDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
@@ -473,4 +483,4 @@ public class PinnedMismatchDialog extends DialogFragment {
             return tabViewGroup;
         }
     }
-}
+}
\ No newline at end of file
index c99eeba00f0b6ad4d6ad3226ac1b55fe0bdc579d..d1ff91c2019f2b0d2303843d8b2721bebf0d5227 100644 (file)
@@ -31,6 +31,7 @@ import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.style.ForegroundColorSpan;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.view.WindowManager;
 import android.widget.TextView;
 
@@ -39,6 +40,9 @@ import androidx.fragment.app.DialogFragment;  // The AndroidX dialog fragment mu
 
 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
 import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
+import com.stoutner.privacybrowser.views.NestedScrollWebView;
+
 import java.text.DateFormat;
 import java.util.Calendar;
 import java.util.Date;
@@ -46,6 +50,23 @@ import java.util.Date;
 // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
 @SuppressLint("InflateParams")
 public class ViewSslCertificateDialog extends DialogFragment {
+    public static ViewSslCertificateDialog displayDialog(long webViewFragmentId) {
+        // Create an arguments bundle.
+        Bundle argumentsBundle = new Bundle();
+
+        // Store the WebView fragment ID in the bundle.
+        argumentsBundle.putLong("webview_fragment_id", webViewFragmentId);
+
+        // Create a new instance of the dialog.
+        ViewSslCertificateDialog viewSslCertificateDialog = new ViewSslCertificateDialog();
+
+        // Add the bundle to the dialog.
+        viewSslCertificateDialog.setArguments(argumentsBundle);
+
+        // Return the new dialog.
+        return viewSslCertificateDialog;
+    }
+
     @NonNull
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         // Remove the incorrect lint warning below that the activity might be null.
@@ -54,6 +75,24 @@ public class ViewSslCertificateDialog extends DialogFragment {
         // Get the activity's layout inflater.
         LayoutInflater layoutInflater = getActivity().getLayoutInflater();
 
+        // Remove the incorrect lint warning below that `getArguments().getLong()` might be null.
+        assert getArguments() != null;
+
+        // Get the current position of this WebView fragment.
+        int webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(getArguments().getLong("webview_fragment_id"));
+
+        // Get the WebView tab fragment.
+        WebViewTabFragment webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition);
+
+        // Get the fragment view.
+        View fragmentView = webViewTabFragment.getView();
+
+        // Remove the incorrect lint warning below that the fragment view might be null.
+        assert fragmentView != null;
+
+        // Get a handle for the current WebView.
+        NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
         // Use a builder to create the alert dialog.
         AlertDialog.Builder dialogBuilder;
 
@@ -73,8 +112,11 @@ public class ViewSslCertificateDialog extends DialogFragment {
         // Set a listener on the negative button.  Using `null` as the listener closes the dialog without doing anything else.
         dialogBuilder.setNegativeButton(R.string.close, null);
 
+        // Get the SSL certificate.
+        SslCertificate sslCertificate = nestedScrollWebView.getCertificate();
+
         // Check to see if the website is encrypted.
-        if (MainWebViewActivity.sslCertificate == null) {  // The website is not encrypted.
+        if (sslCertificate == null) {  // The website is not encrypted.
             // Set the title.
             dialogBuilder.setTitle(R.string.unencrypted_website);
 
@@ -145,9 +187,6 @@ public class ViewSslCertificateDialog extends DialogFragment {
             // Extract the domain name from the URI.
             String domainString = uri.getHost();
 
-            // Get the SSL certificate.
-            SslCertificate sslCertificate = MainWebViewActivity.sslCertificate;
-
             // Get the strings from the SSL certificate.
             String issuedToCName = sslCertificate.getIssuedTo().getCName();
             String issuedToOName = sslCertificate.getIssuedTo().getOName();
@@ -160,7 +199,7 @@ public class ViewSslCertificateDialog extends DialogFragment {
 
             // Create spannable string builders for each text view that needs multiple colors of text.
             SpannableStringBuilder domainStringBuilder = new SpannableStringBuilder(domainLabel + domainString);
-            SpannableStringBuilder ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + MainWebViewActivity.currentHostIpAddresses);
+            SpannableStringBuilder ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + nestedScrollWebView.getCurrentIpAddresses());
             SpannableStringBuilder issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + issuedToCName);
             SpannableStringBuilder issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + issuedToOName);
             SpannableStringBuilder issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + issuedToUName);
index f08f343fcef50cb1e4b495278dba7f3e148d959e..43032f84933c3481c443c95c2e0f16ec3b1c3215 100644 (file)
@@ -53,6 +53,7 @@ import androidx.cardview.widget.CardView;
 import androidx.fragment.app.Fragment;  // The AndroidX fragment must be used until minimum API >= 23.  Otherwise `getContext()` does not work.
 
 import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.activities.DomainsActivity;
 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
 
@@ -182,7 +183,7 @@ public class DomainSettingsFragment extends Fragment {
         String endDateLabel = getString(R.string.end_date) + "  ";
 
         // Get the current website SSL certificate
-        final SslCertificate currentWebsiteSslCertificate = MainWebViewActivity.sslCertificate;
+        final SslCertificate currentWebsiteSslCertificate = DomainsActivity.currentSslCertificate;
 
         // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
         DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0);
@@ -1163,7 +1164,7 @@ public class DomainSettingsFragment extends Fragment {
 
         // Populate the saved and current IP addresses.
         savedIpAddressesTextView.setText(savedIpAddresses);
-        currentIpAddressesTextView.setText(MainWebViewActivity.currentHostIpAddresses);
+        currentIpAddressesTextView.setText(DomainsActivity.currentIpAddresses);
 
         // Set the initial display status of the IP addresses card views.
         if (pinnedIpAddressesSwitch.isChecked()) {  // IP addresses are pinned.
index bbda97990df89de636066b6cdccd8d7378a972af..3ffa8aa4faaa81ab8e8c97c60e715fcdab82c163 100644 (file)
@@ -36,11 +36,11 @@ import java.util.Calendar;
 
 public class WebViewTabFragment extends Fragment {
     // Set a unique ID for this tab based on the time it was created.
-    public long tabId = Calendar.getInstance().getTimeInMillis();
+    public long fragmentId = Calendar.getInstance().getTimeInMillis();
 
     // The public interface is used to send information back to the parent activity.
     public interface NewTabListener {
-        void initializeWebView(long pageId, int pageNumber, ProgressBar progressBar, NestedScrollWebView nestedScrollWebView);
+        void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar);
     }
 
     // The new tab listener is used in `onAttach()` and `onCreateView()`.
@@ -90,8 +90,11 @@ public class WebViewTabFragment extends Fragment {
         NestedScrollWebView nestedScrollWebView = newPageView.findViewById(R.id.nestedscroll_webview);
         ProgressBar progressBar = newPageView.findViewById(R.id.progress_bar);
 
+        // Store the WebView fragment ID in the nested scroll WebView.
+        nestedScrollWebView.setWebViewFragmentId(fragmentId);
+
         // Request the main activity initialize the WebView.
-        newTabListener.initializeWebView(tabId, pageNumber, progressBar, nestedScrollWebView);
+        newTabListener.initializeWebView(nestedScrollWebView, pageNumber, progressBar);
 
         // Return the new page view.
         return newPageView;
diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/CheckPinnedMismatchHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/CheckPinnedMismatchHelper.java
new file mode 100644 (file)
index 0000000..25d8414
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright © 2018-2019 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.helpers;
+
+import android.net.http.SslCertificate;
+
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.FragmentManager;
+
+import com.stoutner.privacybrowser.dialogs.PinnedMismatchDialog;
+import com.stoutner.privacybrowser.views.NestedScrollWebView;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+public class CheckPinnedMismatchHelper {
+    public static void checkPinnedMismatch(FragmentManager fragmentManager, NestedScrollWebView nestedScrollWebView) {
+        // Initialize the current SSL certificate variables.
+        String currentWebsiteIssuedToCName = "";
+        String currentWebsiteIssuedToOName = "";
+        String currentWebsiteIssuedToUName = "";
+        String currentWebsiteIssuedByCName = "";
+        String currentWebsiteIssuedByOName = "";
+        String currentWebsiteIssuedByUName = "";
+        Date currentWebsiteSslStartDate = null;
+        Date currentWebsiteSslEndDate = null;
+
+        // Initialize the pinned SSL certificate variables.
+        String pinnedSslIssuedToCName = "";
+        String pinnedSslIssuedToOName = "";
+        String pinnedSslIssuedToUName = "";
+        String pinnedSslIssuedByCName = "";
+        String pinnedSslIssuedByOName = "";
+        String pinnedSslIssuedByUName = "";
+        Date pinnedSslStartDate = null;
+        Date pinnedSslEndDate = null;
+
+        // Get the current website SSL certificate.
+        SslCertificate currentWebsiteSslCertificate = nestedScrollWebView.getCertificate();
+
+        // Extract the individual pieces of information from the current website SSL certificate if it is not null.
+        if (currentWebsiteSslCertificate != null) {
+            currentWebsiteIssuedToCName = currentWebsiteSslCertificate.getIssuedTo().getCName();
+            currentWebsiteIssuedToOName = currentWebsiteSslCertificate.getIssuedTo().getOName();
+            currentWebsiteIssuedToUName = currentWebsiteSslCertificate.getIssuedTo().getUName();
+            currentWebsiteIssuedByCName = currentWebsiteSslCertificate.getIssuedBy().getCName();
+            currentWebsiteIssuedByOName = currentWebsiteSslCertificate.getIssuedBy().getOName();
+            currentWebsiteIssuedByUName = currentWebsiteSslCertificate.getIssuedBy().getUName();
+            currentWebsiteSslStartDate = currentWebsiteSslCertificate.getValidNotBeforeDate();
+            currentWebsiteSslEndDate = currentWebsiteSslCertificate.getValidNotAfterDate();
+        }
+
+        // Get the pinned SSL certificate information if it exists.
+        if (nestedScrollWebView.hasPinnedSslCertificate()) {
+            // Get the pinned SSL certificate.
+            ArrayList<Object> pinnedSslCertificateArrayList = nestedScrollWebView.getPinnedSslCertificate();
+
+            // Extract the arrays from the array list.
+            String[] pinnedSslCertificateStringArray = (String[]) pinnedSslCertificateArrayList.get(0);
+            Date[] pinnedSslCertificateDateArray = (Date[]) pinnedSslCertificateArrayList.get(1);
+
+            // Populate the pinned SSL certificate string variables.
+            pinnedSslIssuedToCName = pinnedSslCertificateStringArray[0];
+            pinnedSslIssuedToOName = pinnedSslCertificateStringArray[1];
+            pinnedSslIssuedToUName = pinnedSslCertificateStringArray[2];
+            pinnedSslIssuedByCName = pinnedSslCertificateStringArray[3];
+            pinnedSslIssuedByOName = pinnedSslCertificateStringArray[4];
+            pinnedSslIssuedByUName = pinnedSslCertificateStringArray[5];
+
+            // Populate the pinned SSL certificate date variables.
+            pinnedSslStartDate = pinnedSslCertificateDateArray[0];
+            pinnedSslEndDate = pinnedSslCertificateDateArray[1];
+        }
+
+        // Initialize string variables to store the SSL certificate dates.  Strings are needed to compare the values below, which doesn't work with dates if the first one is null.
+        String currentWebsiteSslStartDateString = "";
+        String currentWebsiteSslEndDateString = "";
+        String pinnedSslStartDateString = "";
+        String pinnedSslEndDateString = "";
+
+        // Convert the dates to strings if they are not null.
+        if (currentWebsiteSslStartDate != null) {
+            currentWebsiteSslStartDateString = currentWebsiteSslStartDate.toString();
+        }
+
+        if (currentWebsiteSslEndDate != null) {
+            currentWebsiteSslEndDateString = currentWebsiteSslEndDate.toString();
+        }
+
+        if (pinnedSslStartDate != null) {
+            pinnedSslStartDateString = pinnedSslStartDate.toString();
+        }
+
+        if (pinnedSslEndDate != null) {
+            pinnedSslEndDateString = pinnedSslEndDate.toString();
+        }
+
+        // Check to see if the pinned information matches the current information.
+        if ((nestedScrollWebView.hasPinnedIpAddresses() && !nestedScrollWebView.getCurrentIpAddresses().equals(nestedScrollWebView.getPinnedIpAddresses())) ||
+                (nestedScrollWebView.hasPinnedSslCertificate() && (!currentWebsiteIssuedToCName.equals(pinnedSslIssuedToCName) ||
+                !currentWebsiteIssuedToOName.equals(pinnedSslIssuedToOName) || !currentWebsiteIssuedToUName.equals(pinnedSslIssuedToUName) ||
+                !currentWebsiteIssuedByCName.equals(pinnedSslIssuedByCName) || !currentWebsiteIssuedByOName.equals(pinnedSslIssuedByOName) ||
+                !currentWebsiteIssuedByUName.equals(pinnedSslIssuedByUName) || !currentWebsiteSslStartDateString.equals(pinnedSslStartDateString) ||
+                !currentWebsiteSslEndDateString.equals(pinnedSslEndDateString)))) {
+
+            // Get a handle for the pinned mismatch alert dialog.
+            DialogFragment pinnedMismatchDialogFragment = PinnedMismatchDialog.displayDialog(nestedScrollWebView.getWebViewFragmentId());
+
+            // Show the pinned mismatch alert dialog.
+            pinnedMismatchDialogFragment.show(fragmentManager, "Pinned Mismatch");
+        }
+    }
+}
\ No newline at end of file
index c4f54de9ee2488dc985a6ecdb0ba9fa2b599ccb1..c7e5ade3dee5a2a45dfe5b970bac98c6c10e9a70 100644 (file)
@@ -29,6 +29,7 @@ import androidx.core.view.NestedScrollingChildHelper;
 import androidx.core.view.ViewCompat;
 
 import java.util.ArrayList;
+import java.util.Date;
 
 // NestedScrollWebView extends WebView to handle nested scrolls (scrolling the app bar off the screen).
 public class NestedScrollWebView extends WebView implements NestedScrollingChild2 {
@@ -41,11 +42,8 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild
     public final static int ULTRA_PRIVACY_BLOCKED_REQUESTS = 5;
     public final static int THIRD_PARTY_BLOCKED_REQUESTS = 6;
 
-    // The nested scrolling child helper is used throughout the class.
-    private NestedScrollingChildHelper nestedScrollingChildHelper;
-
-    // The previous Y position needs to be tracked between motion events.
-    private int previousYPosition;
+    // Keep a copy of the WebView fragment ID.
+    private long webViewFragmentId;
 
     // Track if domain settings are applied to this nested scroll WebView and, if so, the database ID.
     private boolean domainSettingsApplied;
@@ -61,19 +59,49 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild
     private int ultraPrivacyBlockedRequests;
     private int thirdPartyBlockedRequests;
 
-    // Basic constructor.
+    // The pinned SSL certificate variables.
+    private boolean hasPinnedSslCertificate;
+    private String pinnedSslIssuedToCName;
+    private String pinnedSslIssuedToOName;
+    private String pinnedSslIssuedToUName;
+    private String pinnedSslIssuedByCName;
+    private String pinnedSslIssuedByOName;
+    private String pinnedSslIssuedByUName;
+    private Date pinnedSslStartDate;
+    private Date pinnedSslEndDate;
+
+    // The current IP addresses variables.
+    private boolean hasCurrentIpAddresses;
+    private String currentIpAddresses;
+
+    // The pinned IP addresses variables.
+    private boolean hasPinnedIpAddresses;
+    private String pinnedIpAddresses;
+
+    // The ignore pinned domain information tracker.  This is set when a user proceeds past a pinned mismatch dialog to prevent the dialog from showing again until after the domain changes.
+    private boolean ignorePinnedDomainInformation;
+
+    // The nested scrolling child helper is used throughout the class.
+    private NestedScrollingChildHelper nestedScrollingChildHelper;
+
+    // The previous Y position needs to be tracked between motion events.
+    private int previousYPosition;
+
+
+
+    // The basic constructor.
     public NestedScrollWebView(Context context) {
         // Roll up to the next constructor.
         this(context, null);
     }
 
-    // Intermediate constructor.
+    // The intermediate constructor.
     public NestedScrollWebView(Context context, AttributeSet attributeSet) {
         // Roll up to the next constructor.
         this(context, attributeSet, android.R.attr.webViewStyle);
     }
 
-    // Full constructor.
+    // The full constructor.
     public NestedScrollWebView(Context context, AttributeSet attributeSet, int defaultStyle) {
         // Run the default commands.
         super(context, attributeSet, defaultStyle);
@@ -85,6 +113,21 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild
         nestedScrollingChildHelper.setNestedScrollingEnabled(true);
     }
 
+
+
+    // WebView Fragment ID.
+    public void setWebViewFragmentId(long webViewFragmentId) {
+        // Store the WebView fragment ID.
+        this.webViewFragmentId = webViewFragmentId;
+    }
+
+    public long getWebViewFragmentId() {
+        // Return the WebView fragment ID.
+        return webViewFragmentId;
+    }
+
+
+    // Domain settings.
     public void setDomainSettingsApplied(boolean applied) {
         // Store the domain settings applied status.
         domainSettingsApplied = applied;
@@ -95,6 +138,8 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild
         return domainSettingsApplied;
     }
 
+
+    // Domain settings database ID.
     public void setDomainSettingsDatabaseId(int databaseId) {
         // Store the domain settings database ID.
         domainSettingsDatabaseId = databaseId;
@@ -105,6 +150,8 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild
         return domainSettingsDatabaseId;
     }
 
+
+    // Resource requests.
     public void addResourceRequest(String[] resourceRequest) {
         // Add the resource request to the list.
         resourceRequests.add(resourceRequest);
@@ -120,6 +167,8 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild
         resourceRequests.clear();
     }
 
+
+    // Resource request counters.
     public void resetRequestsCount(int list) {
         // Run the command on the indicated list.
         switch (list) {
@@ -237,6 +286,132 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild
         }
     }
 
+
+    // Pinned SSL certificates.
+    public boolean hasPinnedSslCertificate() {
+        // Return the status of the pinned SSL certificate.
+        return hasPinnedSslCertificate;
+    }
+
+    public void setPinnedSslCertificate(String issuedToCName, String issuedToOName, String issuedToUName, String issuedByCName, String issuedByOName, String issuedByUName, Date startDate, Date endDate) {
+        // Store the pinned SSL certificate information.
+        pinnedSslIssuedToCName = issuedToCName;
+        pinnedSslIssuedToOName = issuedToOName;
+        pinnedSslIssuedToUName = issuedToUName;
+        pinnedSslIssuedByCName = issuedByCName;
+        pinnedSslIssuedByOName = issuedByOName;
+        pinnedSslIssuedByUName = issuedByUName;
+        pinnedSslStartDate = startDate;
+        pinnedSslEndDate = endDate;
+
+        // Set the pinned SSL certificate tracker.
+        hasPinnedSslCertificate = true;
+    }
+
+    public ArrayList<Object> getPinnedSslCertificate() {
+        // Initialize an array list.
+        ArrayList<Object> arrayList = new ArrayList<>();
+
+        // Create the SSL certificate string array.
+        String[] sslCertificateStringArray = new String[] {pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName};
+
+        // Create the SSL certificate date array.
+        Date[] sslCertificateDateArray = new Date[] {pinnedSslStartDate, pinnedSslEndDate};
+
+        // Add the arrays to the array list.
+        arrayList.add(sslCertificateStringArray);
+        arrayList.add(sslCertificateDateArray);
+
+        // Return the pinned SSL certificate array list.
+        return arrayList;
+    }
+
+    public void clearPinnedSslCertificate() {
+        // Clear the pinned SSL certificate.
+        pinnedSslIssuedToCName = null;
+        pinnedSslIssuedToOName = null;
+        pinnedSslIssuedToUName = null;
+        pinnedSslIssuedByCName = null;
+        pinnedSslIssuedByOName = null;
+        pinnedSslIssuedByUName = null;
+        pinnedSslStartDate = null;
+        pinnedSslEndDate = null;
+
+        // Clear the pinned SSL certificate tracker.
+        hasPinnedSslCertificate = false;
+    }
+
+
+    // Current IP addresses.
+    public boolean hasCurrentIpAddresses() {
+        // Return the status of the current IP addresses.
+        return hasCurrentIpAddresses;
+    }
+
+    public void setCurrentIpAddresses(String ipAddresses) {
+        // Store the current IP addresses.
+        currentIpAddresses = ipAddresses;
+
+        // Set the current IP addresses tracker.
+        hasCurrentIpAddresses = true;
+    }
+
+    public String getCurrentIpAddresses() {
+        // Return the current IP addresses.
+        return currentIpAddresses;
+    }
+
+    public void clearCurrentIpAddresses() {
+        // Clear the current IP addresses.
+        currentIpAddresses = null;
+
+        // Clear the current IP addresses tracker.
+        hasCurrentIpAddresses = false;
+    }
+
+
+    // Pinned IP addresses.
+    public boolean hasPinnedIpAddresses() {
+        // Return the status of the pinned IP addresses.
+        return hasPinnedIpAddresses;
+    }
+
+    public void setPinnedIpAddresses(String ipAddresses) {
+        // Store the pinned IP addresses.
+        pinnedIpAddresses = ipAddresses;
+
+        // Set the pinned IP addresses tracker.
+        hasPinnedIpAddresses = true;
+    }
+
+    public String getPinnedIpAddresses() {
+        // Return the pinned IP addresses.
+        return pinnedIpAddresses;
+    }
+
+    public void clearPinnedIpAddresses() {
+        // Clear the pinned IP addresses.
+        pinnedIpAddresses = null;
+
+        // Clear the pinned IP addresses tracker.
+        hasPinnedIpAddresses = false;
+    }
+
+
+    // Ignore pinned information.  The syntax looks better as written, even if it is always inverted.
+    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+    public boolean ignorePinnedDomainInformation() {
+        // Return the status of the ignore pinned domain information tracker.
+        return ignorePinnedDomainInformation;
+    }
+
+    public void setIgnorePinnedDomainInformation(boolean status) {
+        // Set the status of the ignore pinned domain information tracker.
+        ignorePinnedDomainInformation = status;
+    }
+
+
+
     @Override
     public boolean onTouchEvent(MotionEvent motionEvent) {
         // Initialize a tracker to return if this motion event is handled.
index 0357aaeee5dcc4acce22adee448c2f1c6ca743eb..5be1f9235474e2b456c5a07c06b3205b26fe5607 100644 (file)
@@ -1,13 +1,13 @@
-Privacy Browser защищает вашу конфиденциальность. В нем по умолчанию отключены такие инструменты отслеживания, как JavaScript, хранилище DOM и cookie.  При необходимости их можно оперативно включить во время просмотра сайта, либо добавить правило для нужного домена. Кроме того, в Privacy Browser встроен список блокировки EasyList, который блокирует многие технологии отслеживания даже при включенном JavaScript.
+Большинство браузеров скрытно предоставляют веб-сайтам огромное количество информации, которая позволяет им отслеживать вас и нарушать вашу конфиденциальность. Сайты и рекламные сети используют JavaScript, файлы cookie, DOM-хранилище, пользовательские агенты и многие другие технологии для уникальной идентификации каждого пользователя и его отслеживания при просмотре интернета.
 
-Privacy Browser пока не имеет возможности открытия нескольких вкладок. Появление данной опции запланировано в версии 3.x.
+Privacy Browser разработан для минимизации объема информации, которую браузер представляет веб-сайтам. По умолчанию функции, чувствительные к конфиденциальности, отключены. Если одна из этих технологий необходима для корректной работы веб-сайта, пользователь может включить ее только для этого визита. Кроме того, можно использовать настройки домена, чтобы автоматически включать некоторые функции при входе на определенный веб-сайт и снова отключать их при выходе.
 
\92 Ð½Ð°Ñ\81Ñ\82оÑ\8fÑ\89ее Ð²Ñ\80емÑ\8f Ð² Privacy Browser Ð´Ð»Ñ\8f Ð¾Ñ\82обÑ\80ажениÑ\8f Ð²ÐµÐ±-Ñ\81Ñ\82Ñ\80аниÑ\86 Ð¸Ñ\81полÑ\8cзÑ\83еÑ\82Ñ\81Ñ\8f Ð²Ñ\81Ñ\82Ñ\80оеннÑ\8bй Ð² Android ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ\82 WebView, Ð¿Ð¾Ñ\8dÑ\82омÑ\83 Ð»Ñ\83Ñ\87Ñ\88ие Ñ\80езÑ\83лÑ\8cÑ\82аÑ\82Ñ\8b Ñ\80абоÑ\82Ñ\8b Ð±Ñ\80аÑ\83зеÑ\80а Ð¼Ð¾Ð¶Ð½Ð¾ Ð¿Ð¾Ð»Ñ\83Ñ\87иÑ\82Ñ\8c, Ð¸Ñ\81полÑ\8cзÑ\83Ñ\8f Ð¿Ð¾Ñ\81леднÑ\8eÑ\8e Ð²ÐµÑ\80Ñ\81иÑ\8e Ñ\8dÑ\82ого ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ\82а.  Начиная с версии 4.x Privacy Browser будет переключен на форк WebView под названием Privacy WebView, что позволит использовать более продвинутые функции обеспечения конфиденциальности.
\92 Ð½Ð°Ñ\81Ñ\82оÑ\8fÑ\89ее Ð²Ñ\80емÑ\8f Ð´Ð»Ñ\8f Ð¾Ñ\82обÑ\80ажениÑ\8f Ð²ÐµÐ±-Ñ\81Ñ\82Ñ\80аниÑ\86 Ð² Privacy Browser Ð¸Ñ\81полÑ\8cзÑ\83еÑ\82Ñ\81Ñ\8f Ð²Ñ\81Ñ\82Ñ\80оеннÑ\8bй Ð² Android ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ\82 WebView, Ð¿Ð¾Ñ\8dÑ\82омÑ\83 Ð½Ð°Ð¸Ð»Ñ\83Ñ\87Ñ\88ие Ñ\80езÑ\83лÑ\8cÑ\82аÑ\82Ñ\8b Ñ\80абоÑ\82Ñ\8b Ð±Ñ\80аÑ\83зеÑ\80а Ð¼Ð¾Ð¶Ð½Ð¾ Ð¿Ð¾Ð»Ñ\83Ñ\87иÑ\82Ñ\8c, Ð¸Ñ\81полÑ\8cзÑ\83Ñ\8f Ð¿Ð¾Ñ\81леднÑ\8eÑ\8e Ð²ÐµÑ\80Ñ\81иÑ\8e Ñ\8dÑ\82ого ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ\82а (Ñ\81м. https://www.stoutner.com/privacy-browser/common-settings/webview/). Начиная с версии 4.x Privacy Browser будет переключен на форк WebView под названием Privacy WebView, что позволит использовать более продвинутые функции обеспечения конфиденциальности.
 
-Внимание: из-за ограничений в ОС, при просмотре незащищенных веб-сайтов с устройств под управлением Android KitKat (версия 4.4.x, API 19) Privacy Browser подвержен атакам MITM (человек посередине).  Более подробная информация по этой проблеме доступна на странице https://www.stoutner.com/kitkat-security-problems/.
+Внимание: Android KitKat (версия 4.4.x, API 19) поставляется со старой версией OpenSSL, которая подвержена атакам MITM (человек посередине) при просмотре веб-сайтов, использующих устаревшие протоколы и наборы шифров. Более подробную информацию по данному вопросу можно получить на сайте https://www.stoutner.com/kitkat-security-problems/.
 
-Особенности:
-• Поддержка прокси-сервера Tor Orbot.
+Возможности:
+• Встроенная блокировка рекламы EasyList.
+• Поддержка прокси Tor Orbot.
 • Закрепление SSL-сертификата.
\80¢ Ð\9fолноÑ\8dкÑ\80аннÑ\8bй Ñ\80ежим Ð¿Ñ\80оÑ\81моÑ\82Ñ\80а.
-• Ночной режим.
\80¢ Ð\98мпоÑ\80Ñ\82\8dкÑ\81поÑ\80Ñ\82 Ð½Ð°Ñ\81Ñ\82Ñ\80оек Ð¸ Ð·Ð°ÐºÐ»Ð°Ð´Ð¾Ðº.
\ No newline at end of file