]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
Block trackers listed at privacytests.org. https://redmine.stoutner.com/issues/863
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / MainWebViewActivity.java
index d592b993d7c5fc8f097ee25e0758b0b04d753a04..13d81cb9f5eb103e3e839de96804f2e408c4bb47 100644 (file)
@@ -152,6 +152,7 @@ import com.stoutner.privacybrowser.helpers.BlocklistHelper;
 import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
 import com.stoutner.privacybrowser.helpers.ProxyHelper;
+import com.stoutner.privacybrowser.helpers.SanitizeUrlHelper;
 import com.stoutner.privacybrowser.views.NestedScrollWebView;
 
 import java.io.ByteArrayInputStream;
@@ -182,6 +183,8 @@ import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
+import kotlin.Pair;
+
 public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
         EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, OpenDialog.OpenListener,
         PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveDialog.SaveListener, UrlHistoryDialog.NavigateHistoryListener,
@@ -272,13 +275,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     private int defaultProgressViewStartOffset;
     private int defaultProgressViewEndOffset;
 
-    // The URL sanitizers are set in `applyAppSettings()` and used in `sanitizeUrl()`.
-    private boolean sanitizeGoogleAnalytics;
-    private boolean sanitizeFacebookClickIds;
-    private boolean sanitizeTwitterAmpRedirects;
+    // Declare the helpers.
+    private BookmarksDatabaseHelper bookmarksDatabaseHelper;
+    private DomainsDatabaseHelper domainsDatabaseHelper;
+    private ProxyHelper proxyHelper;
+    private SanitizeUrlHelper sanitizeUrlHelper;
 
     // Declare the class variables
-    private BookmarksDatabaseHelper bookmarksDatabaseHelper;
     private boolean bottomAppBar;
     private boolean displayingFullScreenVideo;
     private boolean downloadWithExternalApp;
@@ -288,9 +291,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     private boolean inFullScreenBrowsingMode;
     private boolean loadingNewIntent;
     private BroadcastReceiver orbotStatusBroadcastReceiver;
-    private ProxyHelper proxyHelper;
     private boolean reapplyAppSettingsOnRestart;
     private boolean reapplyDomainSettingsOnRestart;
+    private boolean sanitizeAmpRedirects;
+    private boolean sanitizeTrackingQueries;
     private boolean scrollAppBar;
     private boolean waitingForProxy;
     private String webViewDefaultUserAgent;
@@ -534,12 +538,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Set the theme.
         setTheme(R.style.PrivacyBrowser);
 
-        // Set the content view.
-        if (bottomAppBar) {
-            setContentView(R.layout.main_framelayout_bottom_appbar);
-        } else {
-            setContentView(R.layout.main_framelayout_top_appbar);
-        }
+        // Set the content view according to the position of the app bar.
+        if (bottomAppBar) setContentView(R.layout.main_framelayout_bottom_appbar);
+        else setContentView(R.layout.main_framelayout_top_appbar);
 
         // Get handles for the views.
         rootFrameLayout = findViewById(R.id.root_framelayout);
@@ -602,8 +603,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Store up to 100 tabs in memory.
         webViewPager.setOffscreenPageLimit(100);
 
-        // Instantiate the proxy helper.
+        // Instantiate the helpers.
+        bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this);
+        domainsDatabaseHelper = new DomainsDatabaseHelper(this);
         proxyHelper = new ProxyHelper();
+        sanitizeUrlHelper = new SanitizeUrlHelper();
 
         // Initialize the app.
         initializeApp();
@@ -1849,24 +1853,45 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             // Consume the event.
             return true;
-        } else if (menuItemId == R.id.share_url) {  // Share URL.
-            // Setup the share string.
+        } else if (menuItemId == R.id.share_message) {  // Share a message.
+            // Prepare the share string.
             String shareString = currentWebView.getTitle() + " – " + currentWebView.getUrl();
 
             // Create the share intent.
-            Intent shareIntent = new Intent(Intent.ACTION_SEND);
+            Intent shareMessageIntent = new Intent(Intent.ACTION_SEND);
 
             // Add the share string to the intent.
-            shareIntent.putExtra(Intent.EXTRA_TEXT, shareString);
+            shareMessageIntent.putExtra(Intent.EXTRA_TEXT, shareString);
 
             // Set the MIME type.
-            shareIntent.setType("text/plain");
+            shareMessageIntent.setType("text/plain");
 
             // Set the intent to open in a new task.
-            shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            shareMessageIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
             // Make it so.
-            startActivity(Intent.createChooser(shareIntent, getString(R.string.share_url)));
+            startActivity(Intent.createChooser(shareMessageIntent, getString(R.string.share_message)));
+
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.share_url) {  // Share URL.
+            // Create the share intent.
+            Intent shareUrlIntent = new Intent(Intent.ACTION_SEND);
+
+            // Add the URL to the intent.
+            shareUrlIntent.putExtra(Intent.EXTRA_TEXT, currentWebView.getUrl());
+
+            // Add the title to the intent.
+            shareUrlIntent.putExtra(Intent.EXTRA_SUBJECT, currentWebView.getTitle());
+
+            // Set the MIME type.
+            shareUrlIntent.setType("text/plain");
+
+            // Set the intent to open in a new task.
+            shareUrlIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            //Make it so.
+            startActivity(Intent.createChooser(shareUrlIntent, getString(R.string.share_url)));
 
             // Consume the event.
             return true;
@@ -1933,9 +1958,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 Uri currentUri = Uri.parse(currentWebView.getUrl());
                 String currentDomain = currentUri.getHost();
 
-                // 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(this, null, null, 0);
-
                 // Create the domain and store the database ID.
                 int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain);
 
@@ -3363,9 +3385,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         drawerLayout.setDrawerTitle(GravityCompat.START, getString(R.string.navigation_drawer));
         drawerLayout.setDrawerTitle(GravityCompat.END, getString(R.string.bookmarks));
 
-        // Initialize the bookmarks database helper.  The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
-        bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this, null, null, 0);
-
         // Initialize `currentBookmarksFolder`.  `""` is the home folder in the database.
         currentBookmarksFolder = "";
 
@@ -3429,7 +3448,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 addNewTab(bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)), false);
 
                 // Display a snackbar.
-                Snackbar.make(currentWebView, R.string.bookmark_opened_in_background, Snackbar.LENGTH_SHORT).show();
+                Snackbar.make(drawerLayout, R.string.bookmark_opened_in_background, Snackbar.LENGTH_SHORT).show();
             }
 
             // Consume the event.
@@ -3500,9 +3519,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         // Store the values from the shared preferences in variables.
         incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
-        sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
-        sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
-        sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
+        sanitizeTrackingQueries = sharedPreferences.getBoolean(getString(R.string.tracking_queries_key), true);
+        sanitizeAmpRedirects = sharedPreferences.getBoolean(getString(R.string.amp_redirects_key), true);
         proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value));
         fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
         downloadWithExternalApp = sharedPreferences.getBoolean(getString(R.string.download_with_external_app_key), false);
@@ -3531,8 +3549,26 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Apply the proxy.
         applyProxy(false);
 
-        // Adjust the layout and scrolling parameters if the app bar is at the top of the screen.
-        if (!bottomAppBar) {
+        // Adjust the layout and scrolling parameters according to the position of the app bar.
+        if (bottomAppBar) {  // The app bar is on the bottom.
+            // Adjust the UI.
+            if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) {  // The app bar scrolls or full screen browsing mode is engaged with the app bar hidden.
+                // Reset the WebView padding to fill the available space.
+                swipeRefreshLayout.setPadding(0, 0, 0, 0);
+            } else {  // The app bar doesn't scroll or full screen browsing mode is not engaged with the app bar hidden.
+                // Move the WebView above the app bar layout.
+                swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight);
+
+                // Show the app bar if it is scrolled off the screen.
+                if (appBarLayout.getTranslationY() != 0) {
+                    // Animate the bottom app bar onto the screen.
+                    objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", 0);
+
+                    // Make it so.
+                    objectAnimator.start();
+                }
+            }
+        } else {  // The app bar is on the top.
             // Get the current layout parameters.  Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command.
             CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
             AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
@@ -3700,10 +3736,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 }
             }
 
-            // 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(this, null, null, 0);
-
-            // Get a full cursor from `domainsDatabaseHelper`.
+            // Get a full domain name cursor.
             Cursor domainNameCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomain();
 
             // Initialize `domainSettingsSet`.
@@ -3712,7 +3745,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Get the domain name column index.
             int domainNameColumnIndex = domainNameCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME);
 
-            // Populate `domainSettingsSet`.
+            // Populate the domain settings set.
             for (int i = 0; i < domainNameCursor.getCount(); i++) {
                 // Move the domains cursor to the current row.
                 domainNameCursor.moveToPosition(i);
@@ -3721,7 +3754,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 domainSettingsSet.add(domainNameCursor.getString(domainNameColumnIndex));
             }
 
-            // Close `domainNameCursor.
+            // Close the domain name cursor.
             domainNameCursor.close();
 
             // Initialize the domain name in database variable.
@@ -3740,7 +3773,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
 
             // Check all the subdomains of the host name against wildcard domains in the domain cursor.
-            while (!nestedScrollWebView.getDomainSettingsApplied() && newHostName.contains(".")) {  // Stop checking if domain settings are already applied or there are no more `.` in the host name.
+            while (!nestedScrollWebView.getDomainSettingsApplied() && newHostName.contains(".")) {  // Stop checking if domain settings are already applied or there are no more `.` in the hostname.
                 if (domainSettingsSet.contains("*." + newHostName)) {  // Check the host name prepended by `*.`.
                     // Set the domain settings applied tracker to true.
                     nestedScrollWebView.setDomainSettingsApplied(true);
@@ -3776,12 +3809,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             String[] userAgentDataArray = getResources().getStringArray(R.array.user_agent_data);
 
             if (nestedScrollWebView.getDomainSettingsApplied()) {  // The url has custom domain settings.
-                // Get a cursor for the current host and move it to the first position.
+                // Remove the incorrect lint warning below that the domain name in database might be null.
+                assert domainNameInDatabase != null;
+
+                // Get a cursor for the current host.
                 Cursor currentDomainSettingsCursor = domainsDatabaseHelper.getCursorForDomainName(domainNameInDatabase);
+
+                // Move to the first position.
                 currentDomainSettingsCursor.moveToFirst();
 
                 // Get the settings from the cursor.
-                nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper._ID)));
+                nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ID)));
                 nestedScrollWebView.getSettings().setJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
                 nestedScrollWebView.setAcceptCookies(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES)) == 1);
                 nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
@@ -4456,49 +4494,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     private String sanitizeUrl(String url) {
-        // Sanitize Google Analytics.
-        if (sanitizeGoogleAnalytics) {
-            // Remove `?utm_`.
-            if (url.contains("?utm_")) {
-                url = url.substring(0, url.indexOf("?utm_"));
-            }
-
-            // Remove `&utm_`.
-            if (url.contains("&utm_")) {
-                url = url.substring(0, url.indexOf("&utm_"));
-            }
-        }
-
-        // Sanitize Facebook Click IDs.
-        if (sanitizeFacebookClickIds) {
-            // Remove `?fbclid=`.
-            if (url.contains("?fbclid=")) {
-                url = url.substring(0, url.indexOf("?fbclid="));
-            }
-
-            // Remove `&fbclid=`.
-            if (url.contains("&fbclid=")) {
-                url = url.substring(0, url.indexOf("&fbclid="));
-            }
-
-            // Remove `?fbadid=`.
-            if (url.contains("?fbadid=")) {
-                url = url.substring(0, url.indexOf("?fbadid="));
-            }
-
-            // Remove `&fbadid=`.
-            if (url.contains("&fbadid=")) {
-                url = url.substring(0, url.indexOf("&fbadid="));
-            }
-        }
+        // Sanitize tracking queries.
+        if (sanitizeTrackingQueries)
+            url = sanitizeUrlHelper.sanitizeTrackingQueries(url);
 
-        // Sanitize Twitter AMP redirects.
-        if (sanitizeTwitterAmpRedirects) {
-            // Remove `?amp=1`.
-            if (url.contains("?amp=1")) {
-                url = url.substring(0, url.indexOf("?amp=1"));
-            }
-        }
+        // Sanitize AMP redirects.
+        if (sanitizeAmpRedirects)
+            url = sanitizeUrlHelper.sanitizeAmpRedirects(url);
 
         // Return the sanitized URL.
         return url;
@@ -5094,7 +5096,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // Toggle the full screen browsing mode.
                     if (inFullScreenBrowsingMode) {  // Switch to full screen mode.
                         // Hide the app bar if specified.
-                        if (hideAppBar) {
+                        if (hideAppBar) {  // The app bar is hidden.
                             // Close the find on page bar if it is visible.
                             closeFindOnPage(null);
 
@@ -5104,8 +5106,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                             // Hide the action bar.
                             actionBar.hide();
 
-                            // Set layout and scrolling parameters if the app bar is at the top of the screen.
-                            if (!bottomAppBar) {
+                            // Set layout and scrolling parameters according to the position of the app bar.
+                            if (bottomAppBar) {  // The app bar is at the bottom.
+                                // Reset the WebView padding to fill the available space.
+                                swipeRefreshLayout.setPadding(0, 0, 0, 0);
+                            } else {  // The app bar is at the top.
                                 // Check to see if the app bar is normally scrolled.
                                 if (scrollAppBar) {  // The app bar is scrolled when it is displayed.
                                     // Get the swipe refresh layout parameters.
@@ -5121,6 +5126,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                                     swipeRefreshLayout.setProgressViewOffset(false, -200, defaultProgressViewEndOffset);
                                 }
                             }
+                        } else {  // The app bar is not hidden.
+                            // Adjust the UI for the bottom app bar.
+                            if (bottomAppBar) {
+                                // Adjust the UI according to the scrolling of the app bar.
+                                if (scrollAppBar) {
+                                    // Reset the WebView padding to fill the available space.
+                                    swipeRefreshLayout.setPadding(0, 0, 0, 0);
+                                } else {
+                                    // Move the WebView above the app bar layout.
+                                    swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight);
+                                }
+                            }
                         }
 
                         /* Hide the system bars.
@@ -5139,23 +5156,32 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                             // Show the action bar.
                             actionBar.show();
+                        }
 
-                            // Set layout and scrolling parameters if the app bar is at the top of the screen.
-                            if (!bottomAppBar) {
-                                // Check to see if the app bar is normally scrolled.
-                                if (scrollAppBar) {  // The app bar is scrolled when it is displayed.
-                                    // Get the swipe refresh layout parameters.
-                                    CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
-
-                                    // Add the off-screen scrolling layout.
-                                    swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
-                                } else {  // The app bar is not scrolled when it is displayed.
-                                    // The swipe refresh layout must be manually moved below the app bar layout.
-                                    swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0);
-
-                                    // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels.
-                                    swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight);
-                                }
+                        // Set layout and scrolling parameters according to the position of the app bar.
+                        if (bottomAppBar) {  // The app bar is at the bottom.
+                            // Adjust the UI.
+                            if (scrollAppBar) {
+                                // Reset the WebView padding to fill the available space.
+                                swipeRefreshLayout.setPadding(0, 0, 0, 0);
+                            } else {
+                                // Move the WebView above the app bar layout.
+                                swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight);
+                            }
+                        } else {  // The app bar is at the top.
+                            // Check to see if the app bar is normally scrolled.
+                            if (scrollAppBar) {  // The app bar is scrolled when it is displayed.
+                                // Get the swipe refresh layout parameters.
+                                CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
+
+                                // Add the off-screen scrolling layout.
+                                swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
+                            } else {  // The app bar is not scrolled when it is displayed.
+                                // The swipe refresh layout must be manually moved below the app bar layout.
+                                swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0);
+
+                                // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels.
+                                swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight);
                             }
                         }
 
@@ -5169,6 +5195,30 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     return false;
                 }
             }
+
+            @Override
+            public boolean onFling(MotionEvent motionEvent1, MotionEvent motionEvent2, float velocityX, float velocityY) {
+                // Scroll the bottom app bar if enabled.
+                if (bottomAppBar && scrollAppBar && !objectAnimator.isRunning()) {
+                    // Calculate the Y change.
+                    float motionY = motionEvent2.getY() - motionEvent1.getY();
+
+                    // Scroll the app bar if the change is greater than 100 pixels.
+                    if (motionY > 50) {
+                        // Animate the bottom app bar onto the screen.
+                        objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", 0);
+                    } else if (motionY < -50) {
+                        // Animate the bottom app bar off the screen.
+                        objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", appBarLayout.getHeight());
+                    }
+
+                    // Make it so.
+                    objectAnimator.start();
+                }
+
+                // Do not consume the event.
+                return false;
+            }
         });
 
         // Pass all touch events on the WebView through the double-tap gesture detector.
@@ -5242,7 +5292,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
         });
 
-        // Update the status of swipe to refresh based on the scroll position of the nested scroll WebView.  Also reinforce full screen browsing mode.
+        // Process scroll changes.
         nestedScrollWebView.setOnScrollChangeListener((view, scrollX, scrollY, oldScrollX, oldScrollY) -> {
             // Set the swipe to refresh status.
             if (nestedScrollWebView.getSwipeToRefresh()) {
@@ -5253,23 +5303,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 swipeRefreshLayout.setEnabled(false);
             }
 
-            //  Scroll the bottom app bar if enabled.
-            if (bottomAppBar && scrollAppBar && !objectAnimator.isRunning()) {
-                if (scrollY < oldScrollY) {  // The WebView was scrolled down.
-                    // Animate the bottom app bar onto the screen.
-                    objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", 0);
-
-                    // Make it so.
-                    objectAnimator.start();
-                } else if (scrollY > oldScrollY) {  // The WebView was scrolled up.
-                    // Animate the bottom app bar off the screen.
-                    objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", appBarLayout.getHeight());
-
-                    // Make it so.
-                    objectAnimator.start();
-                }
-            }
-
             // Reinforce the system UI visibility flags if in full screen browsing mode.
             // This hides the status and navigation bars, which are displayed if other elements are shown, like dialog boxes, the options menu, or the keyboard.
             if (inFullScreenBrowsingMode) {
@@ -5882,8 +5915,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             @Override
             public void onPageStarted(WebView view, String url, Bitmap favicon) {
-                // Set the padding and layout settings if the app bar is at the top of the screen.
-                if (!bottomAppBar) {
+                // Get the app bar layout height.  This can't be done in `applyAppSettings()` because the app bar is not yet populated there.
+                // This should only be populated if it is greater than 0 because otherwise it will be reset to 0 if the app bar is hidden in full screen browsing mode.
+                if (appBarLayout.getHeight() > 0) appBarHeight = appBarLayout.getHeight();
+
+                // Set the padding and layout settings according to the position of the app bar.
+                if (bottomAppBar) {  // The app bar is on the bottom.
+                    // Adjust the UI.
+                    if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) {  // The app bar scrolls or full screen browsing mode is engaged with the app bar hidden.
+                        // Reset the WebView padding to fill the available space.
+                        swipeRefreshLayout.setPadding(0, 0, 0, 0);
+                    } else {  // The app bar doesn't scroll or full screen browsing mode is not engaged with the app bar hidden.
+                        // Move the WebView above the app bar layout.
+                        swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight);
+                    }
+                } else {  // The app bar is on the top.
                     // Set the top padding of the swipe refresh layout according to the app bar scrolling preference.  This can't be done in `appAppSettings()` because the app bar is not yet populated there.
                     if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) {
                         // No padding is needed because it will automatically be placed below the app bar layout due to the scrolling layout behavior.
@@ -5892,9 +5938,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels.
                         swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10, defaultProgressViewEndOffset);
                     } else {
-                        // Get the app bar layout height.  This can't be done in `applyAppSettings()` because the app bar is not yet populated there.
-                        appBarHeight = appBarLayout.getHeight();
-
                         // The swipe refresh layout must be manually moved below the app bar layout.
                         swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0);
 
@@ -6095,11 +6138,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Proceed to the website if the current SSL website certificate matches the pinned domain certificate.
                 if (nestedScrollWebView.hasPinnedSslCertificate()) {
                     // Get the pinned SSL certificate.
-                    ArrayList<Object> pinnedSslCertificateArrayList = nestedScrollWebView.getPinnedSslCertificate();
+                    Pair<String[], Date[]> pinnedSslCertificatePair = nestedScrollWebView.getPinnedSslCertificate();
 
                     // Extract the arrays from the array list.
-                    String[] pinnedSslCertificateStringArray = (String[]) pinnedSslCertificateArrayList.get(0);
-                    Date[] pinnedSslCertificateDateArray = (Date[]) pinnedSslCertificateArrayList.get(1);
+                    String[] pinnedSslCertificateStringArray = pinnedSslCertificatePair.getFirst();
+                    Date[] pinnedSslCertificateDateArray = pinnedSslCertificatePair.getSecond();
 
                     // Check if the current SSL certificate matches the pinned certificate.
                     if (currentWebsiteIssuedToCName.equals(pinnedSslCertificateStringArray[0]) && currentWebsiteIssuedToOName.equals(pinnedSslCertificateStringArray[1]) &&