]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt
First wrong button text in View Headers in night theme. https://redmine.stoutner...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / MainWebViewActivity.kt
index 5235003a9ef563c51d7d4fb372ba31301fa32356..042af73dce4f9258440b6da318dcdbfaac7ba25d 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * Copyright 2015-2023 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2015-2024 Soren Stoutner <soren@stoutner.com>.
  *
  * Download cookie code contributed 2017 Hendrik Knackstedt.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
  *
- * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
  *
  * Privacy Browser Android is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -76,7 +76,6 @@ import android.webkit.WebSettings
 import android.webkit.WebStorage
 import android.webkit.WebView
 import android.webkit.WebViewClient
-import android.webkit.WebViewDatabase
 import android.widget.AdapterView
 import android.widget.ArrayAdapter
 import android.widget.CheckBox
@@ -87,13 +86,13 @@ import android.widget.LinearLayout
 import android.widget.ListView
 import android.widget.ProgressBar
 import android.widget.RadioButton
+import android.widget.RadioGroup
 import android.widget.RelativeLayout
 import android.widget.TextView
 
 import androidx.activity.OnBackPressedCallback
 import androidx.activity.result.contract.ActivityResultContracts
 import androidx.appcompat.app.ActionBar
-import androidx.appcompat.app.ActionBarDrawerToggle
 import androidx.appcompat.app.AppCompatActivity
 import androidx.appcompat.app.AppCompatDelegate
 import androidx.appcompat.content.res.AppCompatResources
@@ -152,7 +151,6 @@ import com.stoutner.privacybrowser.helpers.ENABLE_EASYLIST
 import com.stoutner.privacybrowser.helpers.ENABLE_EASYPRIVACY
 import com.stoutner.privacybrowser.helpers.ENABLE_FANBOYS_ANNOYANCE_LIST
 import com.stoutner.privacybrowser.helpers.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST
-import com.stoutner.privacybrowser.helpers.ENABLE_FORM_DATA
 import com.stoutner.privacybrowser.helpers.ENABLE_JAVASCRIPT
 import com.stoutner.privacybrowser.helpers.ENABLE_ULTRAPRIVACY
 import com.stoutner.privacybrowser.helpers.ENABLED
@@ -252,6 +250,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
 
         // Declare the public static variables.
         lateinit var appBarLayout: AppBarLayout
+        lateinit var defaultFavoriteIconBitmap : Bitmap
     }
 
     // Declare the class variables.
@@ -279,6 +278,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     private lateinit var findOnPageLinearLayout: LinearLayout
     private lateinit var fullScreenVideoFrameLayout: FrameLayout
     private lateinit var initialGrayColorSpan: ForegroundColorSpan
+    private lateinit var inputMethodManager: InputMethodManager
     private lateinit var navigationBackMenuItem: MenuItem
     private lateinit var navigationForwardMenuItem: MenuItem
     private lateinit var navigationHistoryMenuItem: MenuItem
@@ -290,7 +290,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     private lateinit var optionsClearCookiesMenuItem: MenuItem
     private lateinit var optionsClearDataMenuItem: MenuItem
     private lateinit var optionsClearDomStorageMenuItem: MenuItem
-    private lateinit var optionsClearFormDataMenuItem: MenuItem
     private lateinit var optionsCookiesMenuItem: MenuItem
     private lateinit var optionsDarkWebViewMenuItem: MenuItem
     private lateinit var optionsDisplayImagesMenuItem: MenuItem
@@ -308,7 +307,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     private lateinit var optionsProxyNoneMenuItem: MenuItem
     private lateinit var optionsProxyTorMenuItem: MenuItem
     private lateinit var optionsRefreshMenuItem: MenuItem
-    private lateinit var optionsSaveFormDataMenuItem: MenuItem
     private lateinit var optionsSwipeToRefreshMenuItem: MenuItem
     private lateinit var optionsUltraListMenuItem: MenuItem
     private lateinit var optionsUltraPrivacyMenuItem: MenuItem
@@ -320,7 +318,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     private lateinit var optionsUserAgentFirefoxOnAndroidMenuItem: MenuItem
     private lateinit var optionsUserAgentFirefoxOnLinuxMenuItem: MenuItem
     private lateinit var optionsUserAgentFirefoxOnWindowsMenuItem: MenuItem
-    private lateinit var optionsUserAgentInternetExplorerOnWindowsMenuItem: MenuItem
     private lateinit var optionsUserAgentMenuItem: MenuItem
     private lateinit var optionsUserAgentPrivacyBrowserMenuItem: MenuItem
     private lateinit var optionsUserAgentSafariOnIosMenuItem: MenuItem
@@ -345,10 +342,11 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     private lateinit var urlEditText: EditText
     private lateinit var urlRelativeLayout: RelativeLayout
     private lateinit var userAgentDataArray: Array<String>
-    private lateinit var userAgentNamesArray: ArrayAdapter<CharSequence>
+    private lateinit var userAgentNamesArray: Array<String>
+    private lateinit var userAgentDataArrayAdapter: ArrayAdapter<CharSequence>
+    private lateinit var userAgentNamesArrayAdapter: ArrayAdapter<CharSequence>
 
     // Define the class variables.
-    private var actionBarDrawerToggle: ActionBarDrawerToggle? = null
     private var appBarHeight = 0
     private var bookmarksCursor: Cursor? = null
     private var bookmarksDatabaseHelper: BookmarksDatabaseHelper? = null
@@ -363,7 +361,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     private var defaultEasyPrivacy = true
     private var defaultFanboysAnnoyanceList = true
     private var defaultFanboysSocialBlockingList = true
-    private var defaultFormData = false  // Form data can be removed once the minimum API >= 26.
     private var defaultProgressViewEndOffset = 0
     private var defaultProgressViewStartOffset = 0
     private var defaultJavaScript = false
@@ -376,7 +373,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     private var domainsDatabaseHelper: DomainsDatabaseHelper? = null
     private var downloadWithExternalApp = false
     private var fullScreenBrowsingModeEnabled = false
-    private var hideAppBar = false
+    private var hideAppBar = true
     private var inFullScreenBrowsingMode = false
     private var incognitoModeEnabled = false
     private var loadingNewIntent = false
@@ -421,23 +418,17 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     private val saveWebpageArchiveActivityResultLauncher = registerForActivityResult<String, Uri>(ActivityResultContracts.CreateDocument("multipart/related")) { fileUri ->
         // Only save the webpage archive if the file URI is not null, which happens if the user exited the file picker by pressing back.
         if (fileUri != null) {
-            // Initialize the file name string from the file URI last path segment.
-            var fileNameString = fileUri.lastPathSegment
+            // Get a cursor from the content resolver.
+            val contentResolverCursor = contentResolver.query(fileUri, null, null, null)!!
 
-            // Query the exact file name if the API >= 26.
-            if (Build.VERSION.SDK_INT >= 26) {
-                // Get a cursor from the content resolver.
-                val contentResolverCursor = contentResolver.query(fileUri, null, null, null)!!
+            // Move to the fist row.
+            contentResolverCursor.moveToFirst()
 
-                // Move to the fist row.
-                contentResolverCursor.moveToFirst()
+            // Get the file name from the cursor.
+            val fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
 
-                // Get the file name from the cursor.
-                fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
-
-                // Close the cursor.
-                contentResolverCursor.close()
-            }
+            // Close the cursor.
+            contentResolverCursor.close()
 
             // Use a coroutine to save the file.
             CoroutineScope(Dispatchers.Main).launch {
@@ -523,8 +514,17 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
         bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)
         displayAdditionalAppBarIcons = sharedPreferences.getBoolean(getString(R.string.display_additional_app_bar_icons_key), false)
+        val displayUnderCutouts = sharedPreferences.getBoolean(getString(R.string.display_under_cutouts_key), false)
+
+        // Display under cutouts if specified.  This must be done here as it doesn't appear to work correctly if handled after the app is fully initialized.
+        if (displayUnderCutouts) {
+            if (Build.VERSION.SDK_INT >= 30)
+                window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+            else if (Build.VERSION.SDK_INT >= 28)
+                window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
+        }
 
-        // Get the theme entry values string array.
+        // Get the entry values string arrays.
         val appThemeEntryValuesStringArray = resources.getStringArray(R.array.app_theme_entry_values)
 
         // Get the current theme status.
@@ -629,9 +629,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             urlRelativeLayout = findViewById(R.id.url_relativelayout)
             urlEditText = findViewById(R.id.url_edittext)
 
-            // Create the hamburger icon at the start of the AppBar.
-            actionBarDrawerToggle = ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.open_navigation_drawer, R.string.close_navigation_drawer)
-
             // Initially disable the sliding drawers.  They will be enabled once the filter lists are loaded.
             drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
 
@@ -639,9 +636,9 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             drawerLayout.visibility = View.GONE
 
             // Initialize the WebView state adapter.
-            webViewStateAdapter = WebViewStateAdapter(this)
+            webViewStateAdapter = WebViewStateAdapter(this, bottomAppBar)
 
-            // Set the pager adapter on the web view pager.
+            // Set the WebView pager adapter.
             webViewViewPager2.adapter = webViewStateAdapter
 
             // Store up to 100 tabs in memory.
@@ -661,6 +658,15 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             // Update the bookmarks drawer pinned image view.
             updateBookmarksDrawerPinnedImageView()
 
+            // Get the default favorite icon drawable.
+            val favoriteIconDrawable = AppCompatResources.getDrawable(this, R.drawable.world)
+
+            // Cast the favorite icon drawable to a bitmap drawable.
+            val favoriteIconBitmapDrawable = (favoriteIconDrawable as BitmapDrawable?)!!
+
+            // Store the default favorite icon bitmap.
+            defaultFavoriteIconBitmap = favoriteIconBitmapDrawable.bitmap
+
             // Initialize the app.
             initializeApp()
 
@@ -682,20 +688,8 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                         exitFullScreenVideo()
                         // It shouldn't be possible for the currentWebView to be null, but crash logs indicate it sometimes happens.
                     } else if (currentWebView != null && currentWebView!!.canGoBack()) {  // There is at least one item in the current WebView history.
-                        // Get the current web back forward list.
-                        val webBackForwardList = currentWebView!!.copyBackForwardList()
-
-                        // Get the previous entry URL.
-                        val previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.currentIndex - 1).url
-
-                        // Apply the domain settings.
-                        applyDomainSettings(currentWebView!!, previousUrl, resetTab = false, reloadWebsite = false, loadUrl = false)
-
-                        // Go back.
-                        currentWebView!!.goBack()
-
-                        // Update the URL edit text after a delay.
-                        updateUrlEditTextAfterDelay()
+                        // Navigate back one page.
+                        navigateHistory(-1)
                     } else {  // Close the current tab.
                         // A view is required because the method is also called by an XML `onClick`.
                         closeTab(null)
@@ -714,15 +708,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         }
     }
 
-    public override fun onPostCreate(savedInstanceState: Bundle?) {
-        // Run the default commands.
-        super.onPostCreate(savedInstanceState)
-
-        // Sync the state of the DrawerToggle after the default `onRestoreInstanceState()` has finished.  This creates the navigation drawer icon.
-        // If the app is restarting to change the app theme the action bar drawer toggle will not yet be populated.
-        actionBarDrawerToggle?.syncState()
-    }
-
     override fun onNewIntent(intent: Intent) {
         // Run the default commands.
         super.onNewIntent(intent)
@@ -777,7 +762,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     loadingNewIntent = true
 
                     // Add a new tab.
-                    addNewTab(url!!, true)
+                    addNewPage(url!!, adjacent = false, moveToTab = true)
                 } else {  // Load the URL in the current tab.
                     // Make it so.
                     loadUrl(currentWebView!!, url!!)
@@ -1038,11 +1023,9 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         val optionsBookmarksMenuItem = menu.findItem(R.id.bookmarks)
         optionsCookiesMenuItem = menu.findItem(R.id.cookies)
         optionsDomStorageMenuItem = menu.findItem(R.id.dom_storage)
-        optionsSaveFormDataMenuItem = menu.findItem(R.id.save_form_data) // Form data can be removed once the minimum API >= 26.
         optionsClearDataMenuItem = menu.findItem(R.id.clear_data)
         optionsClearCookiesMenuItem = menu.findItem(R.id.clear_cookies)
         optionsClearDomStorageMenuItem = menu.findItem(R.id.clear_dom_storage)
-        optionsClearFormDataMenuItem = menu.findItem(R.id.clear_form_data) // Form data can be removed once the minimum API >= 26.
         optionsEasyListMenuItem = menu.findItem(R.id.easylist)
         optionsEasyPrivacyMenuItem = menu.findItem(R.id.easyprivacy)
         optionsFanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list)
@@ -1067,7 +1050,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         optionsUserAgentFirefoxOnWindowsMenuItem = menu.findItem(R.id.user_agent_firefox_on_windows)
         optionsUserAgentChromeOnWindowsMenuItem = menu.findItem(R.id.user_agent_chrome_on_windows)
         optionsUserAgentEdgeOnWindowsMenuItem = menu.findItem(R.id.user_agent_edge_on_windows)
-        optionsUserAgentInternetExplorerOnWindowsMenuItem = menu.findItem(R.id.user_agent_internet_explorer_on_windows)
         optionsUserAgentSafariOnMacosMenuItem = menu.findItem(R.id.user_agent_safari_on_macos)
         optionsUserAgentCustomMenuItem = menu.findItem(R.id.user_agent_custom)
         optionsSwipeToRefreshMenuItem = menu.findItem(R.id.swipe_to_refresh)
@@ -1081,13 +1063,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         // Set the initial status of the privacy icons.  `false` does not call `invalidateOptionsMenu` as the last step.
         updatePrivacyIcons(false)
 
-        // Only display the form data menu items if the API < 26.
-        optionsSaveFormDataMenuItem.isVisible = Build.VERSION.SDK_INT < 26
-        optionsClearFormDataMenuItem.isVisible = Build.VERSION.SDK_INT < 26
-
-        // Disable the clear form data menu item if the API >= 26 so that the status of the main Clear Data is calculated correctly.
-        optionsClearFormDataMenuItem.isEnabled = Build.VERSION.SDK_INT < 26
-
         // Set the status of the additional app bar icons.  Setting the refresh menu item to `SHOW_AS_ACTION_ALWAYS` makes it appear even on small devices like phones.
         if (displayAdditionalAppBarIcons) {  // Display the additional icons.
             optionsRefreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
@@ -1104,7 +1079,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             // Set the title.
             optionsRefreshMenuItem.setTitle(R.string.stop)
 
-            // Set the icon if it is displayed in the app bar.  Once the minimum API is >= 26, the blue and black icons can be combined with a tint list.
+            // Set the icon if it is displayed in the app bar.
             if (displayAdditionalAppBarIcons)
                 optionsRefreshMenuItem.setIcon(R.drawable.close_blue)
         }
@@ -1137,8 +1112,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
 
             // Set the status of the menu item checkboxes.
             optionsDomStorageMenuItem.isChecked = currentWebView!!.settings.domStorageEnabled
-            @Suppress("DEPRECATION")
-            optionsSaveFormDataMenuItem.isChecked = currentWebView!!.settings.saveFormData  // Form data can be removed once the minimum API >= 26.
             optionsEasyListMenuItem.isChecked = currentWebView!!.easyListEnabled
             optionsEasyPrivacyMenuItem.isChecked = currentWebView!!.easyPrivacyEnabled
             optionsFanboysAnnoyanceListMenuItem.isChecked = currentWebView!!.fanboysAnnoyanceListEnabled
@@ -1208,18 +1181,8 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         // Enable Clear DOM Storage if there is any.
         optionsClearDomStorageMenuItem.isEnabled = localStorageDirectoryNumberOfFiles > 0 || indexedDBDirectoryNumberOfFiles > 0
 
-        // Enable Clear Form Data is there is any.  This can be removed once the minimum API >= 26.
-        if (Build.VERSION.SDK_INT < 26) {
-            // Get the WebView database.
-            val webViewDatabase = WebViewDatabase.getInstance(this)
-
-            // Enable the clear form data menu item if there is anything to clear.
-            @Suppress("DEPRECATION")
-            optionsClearFormDataMenuItem.isEnabled = webViewDatabase.hasFormData()
-        }
-
         // Enable Clear Data if any of the submenu items are enabled.
-        optionsClearDataMenuItem.isEnabled = (optionsClearCookiesMenuItem.isEnabled || optionsClearDomStorageMenuItem.isEnabled || optionsClearFormDataMenuItem.isEnabled)
+        optionsClearDataMenuItem.isEnabled = (optionsClearCookiesMenuItem.isEnabled || optionsClearDomStorageMenuItem.isEnabled)
 
         // Disable Fanboy's Social Blocking List menu item if Fanboy's Annoyance List is checked.
         optionsFanboysSocialBlockingListMenuItem.isEnabled = !optionsFanboysAnnoyanceListMenuItem.isChecked
@@ -1341,15 +1304,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 optionsUserAgentEdgeOnWindowsMenuItem.isChecked = true
             }
 
-            resources.getStringArray(R.array.user_agent_data)[10] -> {  // Internet Explorer on Windows.
-                // Update the user agent menu item title.
-                optionsUserAgentMenuItem.title = getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_internet_explorer_on_windows)
-
-                // Select the Internet on Windows radio box.
-                optionsUserAgentInternetExplorerOnWindowsMenuItem.isChecked = true
-            }
-
-            resources.getStringArray(R.array.user_agent_data)[11] -> {  // Safari on macOS.
+            resources.getStringArray(R.array.user_agent_data)[10] -> {  // Safari on macOS.
                 // Update the user agent menu item title.
                 optionsUserAgentMenuItem.title = getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_safari_on_macos)
 
@@ -1471,32 +1426,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 true
             }
 
-            R.id.save_form_data -> {  // Form data.  This can be removed once the minimum API >= 26.
-                // Switch the status of saveFormDataEnabled.
-                @Suppress("DEPRECATION")
-                currentWebView!!.settings.saveFormData = !currentWebView!!.settings.saveFormData
-
-                // Update the menu checkbox.
-                @Suppress("DEPRECATION")
-                menuItem.isChecked = currentWebView!!.settings.saveFormData
-
-                // Display a snackbar.
-                @Suppress("DEPRECATION")
-                if (currentWebView!!.settings.saveFormData)
-                    Snackbar.make(webViewViewPager2, R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show()
-                else
-                    Snackbar.make(webViewViewPager2, R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show()
-
-                // Update the privacy icon.
-                updatePrivacyIcons(true)
-
-                // Reload the current WebView.
-                currentWebView!!.reload()
-
-                // Consume the event.
-                true
-            }
-
             R.id.clear_cookies -> {  // Clear cookies.
                 // Create a snackbar.
                 Snackbar.make(webViewViewPager2, R.string.cookies_deleted, Snackbar.LENGTH_LONG)
@@ -1572,28 +1501,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 true
             }
 
-            R.id.clear_form_data -> {  // Clear form data.  This can be remove once the minimum API >= 26.
-                // Create a snackbar.
-                Snackbar.make(webViewViewPager2, R.string.form_data_deleted, Snackbar.LENGTH_LONG)
-                    .setAction(R.string.undo) {}  // Everything will be handled by `onDismissed()` below.
-                    .addCallback(object : Snackbar.Callback() {
-                        override fun onDismissed(snackbar: Snackbar, event: Int) {
-                            if (event != DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
-                                // Get a handle for the webView database.
-                                val webViewDatabase = WebViewDatabase.getInstance(applicationContext)
-
-                                // Delete the form data.
-                                @Suppress("DEPRECATION")
-                                webViewDatabase.clearFormData()
-                            }
-                        }
-                    })
-                    .show()
-
-                // Consume the event.
-                true
-            }
-
             R.id.easylist -> {  // EasyList.
                 // Toggle the EasyList status.
                 currentWebView!!.easyListEnabled = !currentWebView!!.easyListEnabled
@@ -1849,20 +1756,9 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 true
             }
 
-            R.id.user_agent_internet_explorer_on_windows -> {  // User Agent - Internet Explorer on Windows.
-                // Update the user agent.
-                currentWebView!!.settings.userAgentString = resources.getStringArray(R.array.user_agent_data)[10]
-
-                // Reload the current WebView.
-                currentWebView!!.reload()
-
-                // Consume the event.
-                true
-            }
-
             R.id.user_agent_safari_on_macos -> {  // User Agent - Safari on macOS.
                 // Update the user agent.
-                currentWebView!!.settings.userAgentString = resources.getStringArray(R.array.user_agent_data)[11]
+                currentWebView!!.settings.userAgentString = resources.getStringArray(R.array.user_agent_data)[10]
 
                 // Reload the current WebView.
                 currentWebView!!.reload()
@@ -1958,9 +1854,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     // Set the focus on the find on page edit text.
                     findOnPageEditText.requestFocus()
 
-                    // Get a handle for the input method manager.
-                    val inputMethodManager = (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager)
-
                     // Display the keyboard.  `0` sets no input flags.
                     inputMethodManager.showSoftInput(findOnPageEditText, 0)
                 }, 200)
@@ -1986,7 +1879,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             R.id.save_url -> {  // Save URL.
                 // Check the download preference.
                 if (downloadWithExternalApp)  // Download with an external app.
-                    downloadUrlWithExternalApp(currentWebView!!.currentUrl)
+                    saveWithExternalApp(currentWebView!!.currentUrl)
                 else  // Handle the download inside of Privacy Browser.  The dialog will be displayed once the file size and the content disposition have been acquired.
                     PrepareSaveDialogCoroutine.prepareSaveDialog(this, supportFragmentManager, currentWebView!!.currentUrl, currentWebView!!.settings.userAgentString, currentWebView!!.acceptCookies)
 
@@ -1995,8 +1888,8 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             }
 
             R.id.save_archive -> {
-                // Open the file picker with a default file name built from the current domain name.
-                saveWebpageArchiveActivityResultLauncher.launch(currentWebView!!.currentDomainName + ".mht")
+                // Open the file picker with a default file name built from the website title.
+                saveWebpageArchiveActivityResultLauncher.launch(currentWebView!!.title + ".mht")
 
                 // Consume the event.
                 true
@@ -2025,10 +1918,10 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Open a new tab according to the current URL.
                 if (currentWebView!!.currentUrl.startsWith("view-source:")) {  // The source is currently viewed.
                     // Open the rendered website in a new tab.
-                    addNewTab(currentWebView!!.currentUrl.substring(12, currentWebView!!.currentUrl.length), true)
+                    addNewPage(currentWebView!!.currentUrl.substring(12, currentWebView!!.currentUrl.length), true, moveToTab = true)
                 } else {  // The rendered website is currently viewed.
                     // Open the source in a new tab.
-                    addNewTab("view-source:${currentWebView!!.currentUrl}", true)
+                    addNewPage("view-source:${currentWebView!!.currentUrl}", adjacent = true, moveToTab = true)
                 }
 
                 // Consume the event.
@@ -2113,7 +2006,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             }
 
             R.id.add_or_edit_domain -> {  // Add or edit domain.
-                // Reapply the domain settings on returning to `MainWebViewActivity`.
+                // Reapply the domain settings on returning to the main WebView activity.
                 reapplyDomainSettingsOnRestart = true
 
                 // Check if domain settings are currently applied.
@@ -2124,7 +2017,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     // Add the extra information to the intent.
                     domainsIntent.putExtra(LOAD_DOMAIN, currentWebView!!.domainSettingsDatabaseId)
                     domainsIntent.putExtra(CLOSE_ON_BACK, true)
-                    domainsIntent.putExtra(CURRENT_URL, currentWebView!!.url)
                     domainsIntent.putExtra(CURRENT_IP_ADDRESSES, currentWebView!!.currentIpAddresses)
 
                     // Get the current certificate.
@@ -2162,8 +2054,105 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     // Get the current domain from the URI.  Use an empty string if it is null.
                     val currentDomain = currentUri.host?: ""
 
+                    // Get the current settings status.
+                    val javaScriptInt = calculateSettingsInt(currentWebView!!.settings.javaScriptEnabled, sharedPreferences.getBoolean(getString(R.string.javascript_key), false))
+                    val cookiesInt = calculateSettingsInt(currentWebView!!.acceptCookies, sharedPreferences.getBoolean(getString(R.string.cookies_key), false))
+                    val domStorageInt = calculateSettingsInt(currentWebView!!.settings.domStorageEnabled, sharedPreferences.getBoolean(getString(R.string.dom_storage_key), false))
+                    val easyListInt = calculateSettingsInt(currentWebView!!.easyListEnabled, sharedPreferences.getBoolean(getString(R.string.easylist_key), true))
+                    val easyPrivacyInt = calculateSettingsInt(currentWebView!!.easyPrivacyEnabled, sharedPreferences.getBoolean(getString(R.string.easyprivacy_key), true))
+                    val fanboysAnnoyanceListInt = calculateSettingsInt(currentWebView!!.fanboysAnnoyanceListEnabled, sharedPreferences.getBoolean(getString(R.string.fanboys_annoyance_list_key), true))
+                    val fanboysSocialBlockingListInt = calculateSettingsInt(currentWebView!!.fanboysSocialBlockingListEnabled, sharedPreferences.getBoolean(getString(R.string.fanboys_social_blocking_list_key), true))
+                    val ultraListInt = calculateSettingsInt(currentWebView!!.ultraListEnabled, sharedPreferences.getBoolean(getString(R.string.ultralist_key), true))
+                    val ultraPrivacyInt = calculateSettingsInt(currentWebView!!.ultraPrivacyEnabled, sharedPreferences.getBoolean(getString(R.string.ultraprivacy_key), true))
+                    val blockAllThirdPartyRequestsInt = calculateSettingsInt(currentWebView!!.blockAllThirdPartyRequests, sharedPreferences.getBoolean(getString(R.string.block_all_third_party_requests_key), true))
+                    val swipeToRefreshInt = calculateSettingsInt(currentWebView!!.swipeToRefresh, sharedPreferences.getBoolean(getString(R.string.swipe_to_refresh_key), true))
+                    val wideViewportInt = calculateSettingsInt(currentWebView!!.settings.useWideViewPort, sharedPreferences.getBoolean(getString(R.string.wide_viewport_key), true))
+                    val displayImagesInt = calculateSettingsInt(currentWebView!!.settings.loadsImagesAutomatically, sharedPreferences.getBoolean(getString(R.string.display_webpage_images_key), true))
+
+                    // Get the current user agent string.
+                    val currentUserAgentString = currentWebView!!.settings.userAgentString
+
+                    // Get the user agent string array position.
+                    val userAgentStringArrayPosition = userAgentDataArrayAdapter.getPosition(currentUserAgentString)
+
+                    // Set the user agent name.
+                    val userAgentName = if ((userAgentStringArrayPosition >= 0) && (defaultUserAgentName == userAgentNamesArray[userAgentStringArrayPosition])) {  // The system default user agent is in use.
+                        getString(R.string.system_default_user_agent)
+                    } else {  // An on-the-fly user agent is being used (or the WebView default user agent is applied).
+                        when (userAgentStringArrayPosition) {
+                            UNRECOGNIZED_USER_AGENT -> { // The user agent is unrecognized.
+                                if (currentUserAgentString == webViewDefaultUserAgent) {  // The WebView default user agent is being used.
+                                    if (defaultUserAgentName == getString(R.string.webview_default)) {  // The WebView default user agent is the system default.
+                                        // Set the user agent name to be the system default.
+                                        getString(R.string.system_default_user_agent)
+                                    } else {  // The WebView default user agent is set as an on-the-fly setting.
+                                        // Set the default user agent name.
+                                        getString(R.string.webview_default)
+                                    }
+                                } else {  // A custom user agent is being used.
+                                    if (defaultUserAgentName == getString(R.string.custom_user_agent_non_translatable)) {  // The system custom user agent is in use.
+                                        // Set the user agent name to be the system default.
+                                        getString(R.string.system_default_user_agent)
+                                    } else {  // An on-the-fly custom user agent is in use.
+                                        // Store the user agent as currently applied.
+                                        currentUserAgentString
+                                    }
+                                }
+                            }
+
+                            else ->  // Store the standard user agent name.
+                                userAgentNamesArray[userAgentStringArrayPosition]
+                        }
+                    }
+
+                    // Get the current text zoom integer.
+                    val textZoomInt = currentWebView!!.settings.textZoom
+
+                    // Set the font size integer.
+                    val fontSizeInt = if (textZoomInt == defaultFontSizeString.toInt())  // The current system default is used, which is encoded as a zoom of `0`.
+                        SYSTEM_DEFAULT
+                    else  // A custom font size is used.
+                        textZoomInt
+
+                    // Get the current WebView dark theme status.
+                    val webViewDarkThemeCurrentlyEnabled = if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING))  // Algorithmic darkening is supported.
+                        WebSettingsCompat.isAlgorithmicDarkeningAllowed(currentWebView!!.settings)
+                    else  // Algorithmic darkening is not supported.
+                        false
+
+                    // Get the default WebView dark theme setting.
+                    val defaultWebViewDarkThemeEnabled = if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {  // Algorithmic darkening is supported.
+                        when (defaultWebViewTheme) {
+                            webViewThemeEntryValuesStringArray[1] ->  // The dark theme is disabled by default.
+                                false
+
+                            webViewThemeEntryValuesStringArray[2] ->  // The dark theme is enabled by default.
+                                true
+
+                            else -> {  // The system default theme is selected.
+                                // Get the current app theme status.
+                                val currentAppThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+
+                                // Check if the current app theme is dark.
+                                currentAppThemeStatus == Configuration.UI_MODE_NIGHT_YES
+                            }
+                        }
+                    } else {  // Algorithmic darkening is not supported.
+                        false
+                    }
+
+                    // Set the WebView theme int.
+                    val webViewThemeInt = if (webViewDarkThemeCurrentlyEnabled == defaultWebViewDarkThemeEnabled)  // The current WebView theme matches the default.
+                        SYSTEM_DEFAULT
+                    else if (webViewDarkThemeCurrentlyEnabled)  // The current WebView theme is dark and that is not the default.
+                        DARK_THEME
+                    else  // The current WebView theme is light and that is not the default.
+                        LIGHT_THEME
+
                     // Create the domain and store the database ID.
-                    val newDomainDatabaseId = domainsDatabaseHelper!!.addDomain(currentDomain)
+                    val newDomainDatabaseId = domainsDatabaseHelper!!.addDomain(currentDomain, javaScriptInt, cookiesInt, domStorageInt, userAgentName, easyListInt, easyPrivacyInt,
+                                                                                fanboysAnnoyanceListInt, fanboysSocialBlockingListInt, ultraListInt, ultraPrivacyInt, blockAllThirdPartyRequestsInt, fontSizeInt,
+                                                                                swipeToRefreshInt, webViewThemeInt, wideViewportInt, displayImagesInt)
 
                     // Create an intent to launch the domains activity.
                     val domainsIntent = Intent(this, DomainsActivity::class.java)
@@ -2171,7 +2160,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     // Add the extra information to the intent.
                     domainsIntent.putExtra(LOAD_DOMAIN, newDomainDatabaseId)
                     domainsIntent.putExtra(CLOSE_ON_BACK, true)
-                    domainsIntent.putExtra(CURRENT_URL, currentWebView!!.url)
                     domainsIntent.putExtra(CURRENT_IP_ADDRESSES, currentWebView!!.currentIpAddresses)
 
                     // Get the current certificate.
@@ -2231,40 +2219,16 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             R.id.back -> {  // Back.
                 // Check if the WebView can go back.
                 if (currentWebView!!.canGoBack()) {
-                    // Get the current web back forward list.
-                    val webBackForwardList = currentWebView!!.copyBackForwardList()
-
-                    // Get the previous entry URL.
-                    val previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.currentIndex - 1).url
-
-                    // Apply the domain settings.
-                    applyDomainSettings(currentWebView!!, previousUrl, resetTab = false, reloadWebsite = false, loadUrl = false)
-
-                    // Load the previous website in the history.
-                    currentWebView!!.goBack()
-
-                    // Update the URL edit text after a delay.
-                    updateUrlEditTextAfterDelay()
+                    // Navigate back one page.
+                    navigateHistory(-1)
                 }
             }
 
             R.id.forward -> {  // Forward.
                 // Check if the WebView can go forward.
                 if (currentWebView!!.canGoForward()) {
-                    // Get the current web back forward list.
-                    val webBackForwardList = currentWebView!!.copyBackForwardList()
-
-                    // Get the next entry URL.
-                    val nextUrl = webBackForwardList.getItemAtIndex(webBackForwardList.currentIndex + 1).url
-
-                    // Apply the domain settings.
-                    applyDomainSettings(currentWebView!!, nextUrl, resetTab = false, reloadWebsite = false, loadUrl = false)
-
-                    // Load the next website in the history.
-                    currentWebView!!.goForward()
-
-                    // Update the URL edit text after a delay.
-                    updateUrlEditTextAfterDelay()
+                    // Navigate forward one page.
+                    navigateHistory(+1)
                 }
             }
 
@@ -2362,7 +2326,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 val domainsIntent = Intent(this, DomainsActivity::class.java)
 
                 // Add the extra information to the intent.
-                domainsIntent.putExtra(CURRENT_URL, currentWebView!!.url)
                 domainsIntent.putExtra(CURRENT_IP_ADDRESSES, currentWebView!!.currentIpAddresses)
 
                 // Get the current certificate.
@@ -2488,7 +2451,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Add an open in new tab entry.
                 contextMenu.add(R.string.open_in_new_tab).setOnMenuItemClickListener {
                     // Load the link URL in a new tab and move to it.
-                    addNewTab(linkUrl, true)
+                    addNewPage(linkUrl, adjacent = true, moveToTab = true)
 
                     // Consume the event.
                     true
@@ -2497,7 +2460,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Add an open in background entry.
                 contextMenu.add(R.string.open_in_background).setOnMenuItemClickListener {
                     // Load the link URL in a new tab but do not move to it.
-                    addNewTab(linkUrl, false)
+                    addNewPage(linkUrl, adjacent = true, moveToTab = false)
 
                     // Consume the event.
                     true
@@ -2537,7 +2500,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 contextMenu.add(R.string.save_url).setOnMenuItemClickListener {
                     // Check the download preference.
                     if (downloadWithExternalApp)  // Download with an external app.
-                        downloadUrlWithExternalApp(linkUrl)
+                        saveWithExternalApp(linkUrl)
                     else  // Handle the download inside of Privacy Browser.  The dialog will be displayed once the file size and the content disposition have been acquired.
                         PrepareSaveDialogCoroutine.prepareSaveDialog(this, supportFragmentManager, linkUrl, currentWebView!!.settings.userAgentString, currentWebView!!.acceptCookies)
 
@@ -2584,7 +2547,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Add an open in new tab entry.
                 contextMenu.add(R.string.open_image_in_new_tab).setOnMenuItemClickListener {
                     // Load the image in a new tab.
-                    addNewTab(imageUrl, true)
+                    addNewPage(imageUrl, adjacent = true, moveToTab = true)
 
                     // Consume the event.
                     true
@@ -2621,7 +2584,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 contextMenu.add(R.string.save_image).setOnMenuItemClickListener {
                     // Check the download preference.
                     if (downloadWithExternalApp) {  // Download with an external app.
-                        downloadUrlWithExternalApp(imageUrl)
+                        saveWithExternalApp(imageUrl)
                     } else {  // Handle the download inside of Privacy Browser.  The dialog will be displayed once the file size and the content disposition have been acquired.
                         PrepareSaveDialogCoroutine.prepareSaveDialog(this, supportFragmentManager, imageUrl, currentWebView!!.settings.userAgentString, currentWebView!!.acceptCookies)
                     }
@@ -2690,7 +2653,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Add an open in new tab entry.
                 contextMenu.add(R.string.open_in_new_tab).setOnMenuItemClickListener {
                     // Load the link URL in a new tab and move to it.
-                    addNewTab(linkUrl, true)
+                    addNewPage(linkUrl, adjacent = true, moveToTab = true)
 
                     // Consume the event.
                     true
@@ -2699,7 +2662,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Add an open in background entry.
                 contextMenu.add(R.string.open_in_background).setOnMenuItemClickListener {
                     // Lod the link URL in a new tab but do not move to it.
-                    addNewTab(linkUrl, false)
+                    addNewPage(linkUrl, adjacent = true, moveToTab = false)
 
                     // Consume the event.
                     true
@@ -2708,7 +2671,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Add an open image in new tab entry.
                 contextMenu.add(R.string.open_image_in_new_tab).setOnMenuItemClickListener {
                     // Load the image in a new tab and move to it.
-                    addNewTab(imageUrl, true)
+                    addNewPage(imageUrl, adjacent = true, moveToTab = true)
 
                     // Consume the event.
                     true
@@ -2745,7 +2708,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 contextMenu.add(R.string.save_image).setOnMenuItemClickListener {
                     // Check the download preference.
                     if (downloadWithExternalApp)  // Download with an external app.
-                        downloadUrlWithExternalApp(imageUrl)
+                        saveWithExternalApp(imageUrl)
                     else  // Handle the download inside of Privacy Browser.  The dialog will be displayed once the file size and the content disposition have been acquired.
                         PrepareSaveDialogCoroutine.prepareSaveDialog(this, supportFragmentManager, imageUrl, currentWebView!!.settings.userAgentString, currentWebView!!.acceptCookies)
 
@@ -2790,7 +2753,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 contextMenu.add(R.string.save_url).setOnMenuItemClickListener {
                     // Check the download preference.
                     if (downloadWithExternalApp)  // Download with an external app.
-                        downloadUrlWithExternalApp(linkUrl)
+                        saveWithExternalApp(linkUrl)
                     else  // Handle the download inside of Privacy Browser.  The dialog will be displayed once the file size and the content disposition have been acquired.
                         PrepareSaveDialogCoroutine.prepareSaveDialog(this, supportFragmentManager, linkUrl, currentWebView!!.settings.userAgentString, currentWebView!!.acceptCookies)
 
@@ -2874,38 +2837,72 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     // The view parameter cannot be removed because it is called from the layout onClick.
     fun addTab(@Suppress("UNUSED_PARAMETER")view: View?) {
         // Add a new tab with a blank URL.
-        addNewTab("", true)
+        addNewPage(urlString = "", adjacent = true, moveToTab = true)
     }
 
-    private fun addNewTab(urlString: String, moveToTab: Boolean) {
+    private fun addNewPage(urlString: String, adjacent: Boolean, moveToTab: Boolean) {
         // Clear the focus from the URL edit text, so that it will be populated with the information from the new tab.
         urlEditText.clearFocus()
 
-        // Get the new page number.  The page numbers are 0 indexed, so the new page number will match the current count.
-        val newTabNumber = tabLayout.tabCount
+        // Add the new tab after the tab layout has quiesced.
+        // Otherwise, there can be problems when restoring a large number of tabs and processing a new intent at the same time.  <https://redmine.stoutner.com/issues/1136>
+        tabLayout.post {
+            // Get the new tab position.
+            val newTabPosition = if (adjacent)  // The new tab position is immediately to the right of the current tab position.
+                tabLayout.selectedTabPosition + 1
+            else  // The new tab position is at the end.  The tab positions are 0 indexed, so the new page number will match the current count.
+                tabLayout.tabCount
 
-        // Add a new tab.
-        tabLayout.addTab(tabLayout.newTab())
+            // Add the new WebView page.
+            webViewStateAdapter!!.addPage(newTabPosition, urlString)
 
-        // Get the new tab.
-        val newTab = tabLayout.getTabAt(newTabNumber)!!
+            // Add the new tab.
+            addNewTab(newTabPosition, moveToTab)
+        }
+    }
 
-        // Set a custom view on the new tab.
-        newTab.setCustomView(R.layout.tab_custom_view)
+    private fun addNewTab(newTabPosition: Int, moveToTab: Boolean) {
+        // Check to see if the new page is ready.
+        if (webViewStateAdapter!!.itemCount >= tabLayout.tabCount) {  // The new page is ready.
+            // Create a new tab.
+            val newTab = tabLayout.newTab()
 
-        // Scroll to the new tab position.
-        tabLayout.post { tabLayout.setScrollPosition(newTabNumber, 0F, false, false) }
+            // Set a custom view on the new tab.
+            newTab.setCustomView(R.layout.tab_custom_view)
 
-        // Add the new WebView page.
-        webViewStateAdapter!!.addPage(newTabNumber, newTab, urlString, moveToTab)
+            // Add the new tab.
+            tabLayout.addTab(newTab, newTabPosition, moveToTab)
 
-        // Show the app bar if it is at the bottom of the screen and the new tab is taking focus.
-        if (bottomAppBar && moveToTab && appBarLayout.translationY != 0f) {
-            // Animate the bottom app bar onto the screen.
-            objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", 0f)
+            // Select the new tab if it is the first one.  For some odd reason, Android doesn't select the first tab if it is the only one, which causes problems with the new tab position logic above.
+            if (newTabPosition == 0)
+                tabLayout.selectTab(newTab)
 
-            // Make it so.
-            objectAnimator.start()
+            // Scroll to the new tab position if moving to the new tab.
+            if (moveToTab)
+                tabLayout.post {
+                    tabLayout.setScrollPosition(newTabPosition, 0F, false, false)
+                }
+
+            // Show the app bar if it is at the bottom of the screen and the new tab is taking focus.
+            if (bottomAppBar && moveToTab && appBarLayout.translationY != 0f) {
+                // Animate the bottom app bar onto the screen.
+                objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", 0f)
+
+                // Make it so.
+                objectAnimator.start()
+            }
+        } else {  // The new page is not ready.
+            // Create a new tab handler.
+            val newTabHandler = Handler(Looper.getMainLooper())
+
+            // Create a new tab runnable.
+            val newTabRunnable = Runnable {
+                // Create the new tab.
+                addNewTab(newTabPosition, moveToTab)
+            }
+
+            // Try adding the new tab again after 50 milliseconds.
+            newTabHandler.postDelayed(newTabRunnable, 50)
         }
     }
 
@@ -2914,7 +2911,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         defaultJavaScript = sharedPreferences.getBoolean(getString(R.string.javascript_key), false)
         defaultCookies = sharedPreferences.getBoolean(getString(R.string.cookies_key), false)
         defaultDomStorage = sharedPreferences.getBoolean(getString(R.string.dom_storage_key), false)
-        defaultFormData = sharedPreferences.getBoolean(getString(R.string.save_form_data_key), false)  // Form data can be removed once the minimum API >= 26.
         defaultEasyList = sharedPreferences.getBoolean(getString(R.string.easylist_key), true)
         defaultEasyPrivacy = sharedPreferences.getBoolean(getString(R.string.easyprivacy_key), true)
         defaultFanboysAnnoyanceList = sharedPreferences.getBoolean(getString(R.string.fanboys_annoyance_list_key), true)
@@ -2929,12 +2925,15 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         defaultWideViewport = sharedPreferences.getBoolean(getString(R.string.wide_viewport_key), true)
         defaultDisplayWebpageImages = sharedPreferences.getBoolean(getString(R.string.display_webpage_images_key), true)
 
-        // Get the WebView theme entry values string array.  This is done here so that expensive resource requests are not made each time a domain is loaded.
+        // Get the string arrays.  These are done here so that expensive resource requests are not made each time a domain is loaded.
         webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values)
-
-        // Get the user agent array adapter and string array.  These are done here so that expensive resource requests are not made each time a domain is loaded.
-        userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item)
         userAgentDataArray = resources.getStringArray(R.array.user_agent_data)
+        userAgentNamesArray = resources.getStringArray(R.array.user_agent_names)
+        val downloadProviderEntryValuesStringArray = resources.getStringArray(R.array.download_provider_entry_values)
+
+        // Get the user agent array adapters.  These are done here so that expensive resource requests are not made each time a domain is loaded.
+        userAgentDataArrayAdapter = ArrayAdapter.createFromResource(this, R.array.user_agent_data, R.layout.spinner_item)
+        userAgentNamesArrayAdapter = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item)
 
         // Store the values from the shared preferences in variables.
         incognitoModeEnabled = sharedPreferences.getBoolean(getString(R.string.incognito_mode_key), false)
@@ -2943,8 +2942,11 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         proxyMode = sharedPreferences.getString(getString(R.string.proxy_key), getString(R.string.proxy_default_value))!!
         fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean(getString(R.string.full_screen_browsing_mode_key), false)
         hideAppBar = sharedPreferences.getBoolean(getString(R.string.hide_app_bar_key), true)
-        downloadWithExternalApp = sharedPreferences.getBoolean(getString(R.string.download_with_external_app_key), false)
-        scrollAppBar = sharedPreferences.getBoolean(getString(R.string.scroll_app_bar_key), true)
+        val downloadProvider = sharedPreferences.getString(getString(R.string.download_provider_key), getString(R.string.download_provider_default_value))!!
+        scrollAppBar = sharedPreferences.getBoolean(getString(R.string.scroll_app_bar_key), false)
+
+        // Determine if downloading should be handled by an external app.
+        downloadWithExternalApp = (downloadProvider == downloadProviderEntryValuesStringArray[2])
 
         // Apply the saved proxy mode if the app has been restarted.
         if (savedProxyMode != null) {
@@ -3101,10 +3103,10 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             nestedScrollWebView.clearPinnedSslCertificate()
             nestedScrollWebView.pinnedIpAddresses = ""
 
-            // Reset the favorite icon if specified.
+            // Reset the tab if specified.
             if (resetTab) {
                 // Initialize the favorite icon.
-                nestedScrollWebView.initializeFavoriteIcon()
+                nestedScrollWebView.resetFavoriteIcon()
 
                 // Get the current page position.
                 val currentPagePosition = webViewStateAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
@@ -3121,8 +3123,12 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     val tabFavoriteIconImageView = tabCustomView.findViewById<ImageView>(R.id.favorite_icon_imageview)
                     val tabTitleTextView = tabCustomView.findViewById<TextView>(R.id.title_textview)
 
+                    // Store the current values in case they need to be restored.
+                    nestedScrollWebView.previousFavoriteIconDrawable = tabFavoriteIconImageView.drawable
+                    nestedScrollWebView.previousWebpageTitle = tabTitleTextView.text.toString()
+
                     // Set the default favorite icon as the favorite icon for this tab.
-                    tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(nestedScrollWebView.getFavoriteIcon(), 64, 64, true))
+                    tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(nestedScrollWebView.getFavoriteIcon(), 128, 128, true))
 
                     // Set the loading title text.
                     tabTitleTextView.setText(R.string.loading)
@@ -3171,7 +3177,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 val javaScriptInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ENABLE_JAVASCRIPT))
                 val cookiesInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(COOKIES))
                 val domStorageInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ENABLE_DOM_STORAGE))
-                val formDataInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ENABLE_FORM_DATA))  // Form data can be removed once the minimum API >= 26.
                 val easyListInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ENABLE_EASYLIST))
                 val easyPrivacyInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ENABLE_EASYPRIVACY))
                 val fanboysAnnoyanceListInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ENABLE_FANBOYS_ANNOYANCE_LIST))
@@ -3224,17 +3229,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     DISABLED -> nestedScrollWebView.settings.domStorageEnabled = false
                 }
 
-                // Apply the form data setting if the API < 26.
-                @Suppress("DEPRECATION")
-                if (Build.VERSION.SDK_INT < 26) {
-                    // Set the form data status.
-                    when (formDataInt) {
-                        SYSTEM_DEFAULT -> nestedScrollWebView.settings.saveFormData = defaultFormData
-                        ENABLED -> nestedScrollWebView.settings.saveFormData = true
-                        DISABLED -> nestedScrollWebView.settings.saveFormData = false
-                    }
-                }
-
                 // Set the EasyList status.
                 when (easyListInt) {
                     SYSTEM_DEFAULT -> nestedScrollWebView.easyListEnabled = defaultEasyList
@@ -3287,23 +3281,22 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Set the user agent.
                 if (userAgentName == getString(R.string.system_default_user_agent)) {  // Use the system default user agent.
                     // Set the user agent according to the system default.
-                    when (val defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName)) {
-                        // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
-                        UNRECOGNIZED_USER_AGENT -> nestedScrollWebView.settings.userAgentString = defaultUserAgentName
+                    when (val defaultUserAgentArrayPosition = userAgentNamesArrayAdapter.getPosition(defaultUserAgentName)) {
+                        UNRECOGNIZED_USER_AGENT ->  // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
+                            nestedScrollWebView.settings.userAgentString = defaultUserAgentName
 
-                        // Set the user agent to `""`, which uses the default value.
-                        SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> nestedScrollWebView.settings.userAgentString = ""
+                        SETTINGS_WEBVIEW_DEFAULT_USER_AGENT ->  // Set the user agent to `""`, which uses the default value.
+                            nestedScrollWebView.settings.userAgentString = ""
 
-                        // Set the default custom user agent.
-                        SETTINGS_CUSTOM_USER_AGENT -> nestedScrollWebView.settings.userAgentString =
-                            sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value))
+                        SETTINGS_CUSTOM_USER_AGENT ->  // Set the default custom user agent.
+                            nestedScrollWebView.settings.userAgentString = sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value))
 
-                        // Get the user agent string from the user agent data array
-                        else -> nestedScrollWebView.settings.userAgentString = userAgentDataArray[defaultUserAgentArrayPosition]
+                        else ->  // Get the user agent string from the user agent data array
+                            nestedScrollWebView.settings.userAgentString = userAgentDataArray[defaultUserAgentArrayPosition]
                     }
                 } else {  // Set the user agent according to the stored name.
                     // Set the user agent.
-                    when (val userAgentArrayPosition = userAgentNamesArray.getPosition(userAgentName)) {
+                    when (val userAgentArrayPosition = userAgentNamesArrayAdapter.getPosition(userAgentName)) {
                         // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
                         UNRECOGNIZED_USER_AGENT ->
                             nestedScrollWebView.settings.userAgentString = userAgentName
@@ -3374,17 +3367,15 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
                     // Set the WebView theme.
                     when (webViewThemeInt) {
-                        // Set the WebView theme.
-                        SYSTEM_DEFAULT ->
+                        SYSTEM_DEFAULT ->  // Set the WebView theme.
                             when (defaultWebViewTheme) {
-                                // The light theme is selected.  Turn off algorithmic darkening.
-                                webViewThemeEntryValuesStringArray[1] -> WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, false)
+                                webViewThemeEntryValuesStringArray[1] ->  // The light theme is selected.  Turn off algorithmic darkening.
+                                    WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, false)
 
-                                // The dark theme is selected.  Turn on algorithmic darkening.
-                                webViewThemeEntryValuesStringArray[2] -> WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, true)
+                                webViewThemeEntryValuesStringArray[2] ->  // The dark theme is selected.  Turn on algorithmic darkening.
+                                    WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, true)
 
-                                // The system default theme is selected.
-                                else -> {
+                                else -> {  // The system default theme is selected.
                                     // Get the current system theme status.
                                     val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
 
@@ -3393,11 +3384,11 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                                 }
                             }
 
-                        // Turn off algorithmic darkening.
-                        LIGHT_THEME -> WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, false)
+                        LIGHT_THEME ->  // Turn off algorithmic darkening.
+                            WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, false)
 
-                        // Turn on algorithmic darkening.
-                        DARK_THEME -> WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, true)
+                        DARK_THEME ->  // Turn on algorithmic darkening.
+                            WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, true)
                     }
                 }
 
@@ -3442,11 +3433,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Apply the default cookie setting.
                 cookieManager.setAcceptCookie(nestedScrollWebView.acceptCookies)
 
-                // Apply the form data setting if the API < 26.
-                if (Build.VERSION.SDK_INT < 26)
-                    @Suppress("DEPRECATION")
-                    nestedScrollWebView.settings.saveFormData = defaultFormData
-
                 // Apply the default font size setting.
                 try {
                     // Try to set the font size from the value in the app settings.
@@ -3475,33 +3461,31 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 nestedScrollWebView.domainSettingsDatabaseId = -1
 
                 // Set the user agent.
-                when (val userAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName)) {
-                    // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
-                    UNRECOGNIZED_USER_AGENT -> nestedScrollWebView.settings.userAgentString = defaultUserAgentName
+                when (val userAgentArrayPosition = userAgentNamesArrayAdapter.getPosition(defaultUserAgentName)) {
+                    UNRECOGNIZED_USER_AGENT ->  // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
+                        nestedScrollWebView.settings.userAgentString = defaultUserAgentName
 
-                    // Set the user agent to `""`, which uses the default value.
-                    SETTINGS_WEBVIEW_DEFAULT_USER_AGENT -> nestedScrollWebView.settings.userAgentString = ""
+                    SETTINGS_WEBVIEW_DEFAULT_USER_AGENT ->  // Set the user agent to `""`, which uses the default value.
+                        nestedScrollWebView.settings.userAgentString = ""
 
-                    // Set the default custom user agent.
-                    SETTINGS_CUSTOM_USER_AGENT -> nestedScrollWebView.settings.userAgentString =
-                        sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value))
+                    SETTINGS_CUSTOM_USER_AGENT ->  // Set the default custom user agent.
+                        nestedScrollWebView.settings.userAgentString = sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value))
 
-                    // Get the user agent string from the user agent data array
-                    else -> nestedScrollWebView.settings.userAgentString = userAgentDataArray[userAgentArrayPosition]
+                    else ->  // Get the user agent string from the user agent data array
+                        nestedScrollWebView.settings.userAgentString = userAgentDataArray[userAgentArrayPosition]
                 }
 
                 // Set the WebView theme if algorithmic darkening is supported.
                 if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
                     // Set the WebView theme.
                     when (defaultWebViewTheme) {
-                        // The light theme is selected.  Turn off algorithmic darkening.
-                        webViewThemeEntryValuesStringArray[1] -> WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, false)
+                        webViewThemeEntryValuesStringArray[1] ->  // The light theme is selected.  Turn off algorithmic darkening.
+                            WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, false)
 
-                        // The dark theme is selected.  Turn on algorithmic darkening.
-                        webViewThemeEntryValuesStringArray[2] -> WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, true)
+                        webViewThemeEntryValuesStringArray[2] ->  // The dark theme is selected.  Turn on algorithmic darkening.
+                            WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, true)
 
-                        // The system default theme is selected.  Get the current system theme status.
-                        else -> {
+                        else -> {  // The system default theme is selected.  Get the current system theme status.
                             // Get the current theme status.
                             val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
 
@@ -3683,6 +3667,15 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         }
     }
 
+    private fun calculateSettingsInt(settingCurrentlyEnabled: Boolean, settingEnabledByDefault: Boolean): Int {
+        return if (settingCurrentlyEnabled == settingEnabledByDefault)  // The current system default is used.
+            SYSTEM_DEFAULT
+        else if (settingCurrentlyEnabled)  // The setting is enabled, which is different from the system default.
+            ENABLED
+        else  // The setting is disabled, which is different from the system default.
+            DISABLED
+    }
+
     private fun clearAndExit() {
         // Close the bookmarks cursor if it exists.
         bookmarksCursor?.close()
@@ -3750,26 +3743,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             }
         }
 
-        // Clear form data if the API < 26.
-        if (Build.VERSION.SDK_INT < 26 && (clearEverything || sharedPreferences.getBoolean(getString(R.string.clear_form_data_key), true))) {
-            // Ask the WebView database to clear the form data.
-            @Suppress("DEPRECATION")
-            WebViewDatabase.getInstance(this).clearFormData()
-
-            // Manually delete the form data database, as the WebView database sometimes will not flush its changes to disk before system exit is run.
-            try {
-                // A string array must be used because the database contains a space and `Runtime.exec` will not otherwise escape the string correctly.
-                val deleteWebDataProcess = runtime.exec(arrayOf("rm", "-f", "$privateDataDirectoryString/app_webview/Web Data"))
-                val deleteWebDataJournalProcess = runtime.exec(arrayOf("rm", "-f", "$privateDataDirectoryString/app_webview/Web Data-journal"))
-
-                // Wait until the processes have finished.
-                deleteWebDataProcess.waitFor()
-                deleteWebDataJournalProcess.waitFor()
-            } catch (exception: Exception) {
-                // Do nothing if an error is thrown.
-            }
-        }
-
         // Clear the logcat.
         if (clearEverything || sharedPreferences.getBoolean(getString(R.string.clear_logcat_key), true)) {
             try {
@@ -3884,9 +3857,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         // Show the toolbar.
         toolbar.visibility = View.VISIBLE
 
-        // Get a handle for the input method manager.
-        val inputMethodManager = (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager)
-
         // Hide the keyboard.
         inputMethodManager.hideSoftInputFromWindow(toolbar.windowToken, 0)
     }
@@ -3910,17 +3880,32 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         }
     }
 
-    override fun createBookmark(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap) {
+    override fun createBookmark(dialogFragment: DialogFragment) {
         // Get the dialog.
         val dialog = dialogFragment.dialog!!
 
         // Get the views from the dialog fragment.
-        val createBookmarkNameEditText = dialog.findViewById<EditText>(R.id.create_bookmark_name_edittext)
-        val createBookmarkUrlEditText = dialog.findViewById<EditText>(R.id.create_bookmark_url_edittext)
+        val webpageFavoriteIconRadioButton = dialog.findViewById<RadioButton>(R.id.webpage_favorite_icon_radiobutton)
+        val webpageFavoriteIconImageView = dialog.findViewById<ImageView>(R.id.webpage_favorite_icon_imageview)
+        val customIconImageView = dialog.findViewById<ImageView>(R.id.custom_icon_imageview)
+        val bookmarkNameEditText = dialog.findViewById<EditText>(R.id.bookmark_name_edittext)
+        val bookmarkUrlEditText = dialog.findViewById<EditText>(R.id.bookmark_url_edittext)
 
         // Extract the strings from the edit texts.
-        val bookmarkNameString = createBookmarkNameEditText.text.toString()
-        val bookmarkUrlString = createBookmarkUrlEditText.text.toString()
+        val bookmarkNameString = bookmarkNameEditText.text.toString()
+        val bookmarkUrlString = bookmarkUrlEditText.text.toString()
+
+        // Get the selected favorite icon drawable.
+        val favoriteIconDrawable = if (webpageFavoriteIconRadioButton.isChecked)  // Use the webpage favorite icon.
+            webpageFavoriteIconImageView.drawable
+        else  // Use the custom icon.
+            customIconImageView.drawable
+
+        // Cast the favorite icon bitmap to a bitmap drawable
+        val favoriteIconBitmapDrawable = favoriteIconDrawable as BitmapDrawable
+
+        // Convert the favorite icon bitmap drawable to a bitmap.
+        val favoriteIconBitmap = favoriteIconBitmapDrawable.bitmap
 
         // Create a favorite icon byte array output stream.
         val favoriteIconByteArrayOutputStream = ByteArrayOutputStream()
@@ -3947,32 +3932,34 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         bookmarksListView.setSelection(newBookmarkDisplayOrder)
     }
 
-    override fun createBookmarkFolder(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap) {
+    override fun createBookmarkFolder(dialogFragment: DialogFragment) {
         // Get the dialog.
         val dialog = dialogFragment.dialog!!
 
         // Get handles for the views in the dialog fragment.
+        val defaultFolderIconRadioButton = dialog.findViewById<RadioButton>(R.id.default_folder_icon_radiobutton)
+        val defaultFolderIconImageView = dialog.findViewById<ImageView>(R.id.default_folder_icon_imageview)
+        val webpageFavoriteIconRadioButton = dialog.findViewById<RadioButton>(R.id.webpage_favorite_icon_radiobutton)
+        val webpageFavoriteIconImageView = dialog.findViewById<ImageView>(R.id.webpage_favorite_icon_imageview)
+        val customIconImageView = dialog.findViewById<ImageView>(R.id.custom_icon_imageview)
         val folderNameEditText = dialog.findViewById<EditText>(R.id.folder_name_edittext)
-        val defaultIconRadioButton = dialog.findViewById<RadioButton>(R.id.default_icon_radiobutton)
-        val defaultIconImageView = dialog.findViewById<ImageView>(R.id.default_icon_imageview)
 
         // Get new folder name string.
         val folderNameString = folderNameEditText.text.toString()
 
         // Set the folder icon bitmap according to the dialog.
-        val folderIconBitmap: Bitmap = if (defaultIconRadioButton.isChecked) {  // Use the default folder icon.
-            // Get the default folder icon drawable.
-            val folderIconDrawable = defaultIconImageView.drawable
-
-            // Convert the folder icon drawable to a bitmap drawable.
-            val folderIconBitmapDrawable = folderIconDrawable as BitmapDrawable
-
-            // Convert the folder icon bitmap drawable to a bitmap.
-            folderIconBitmapDrawable.bitmap
-        } else {  // Use the WebView favorite icon.
-            // Copy the favorite icon bitmap to the folder icon bitmap.
-            favoriteIconBitmap
-        }
+        val folderIconDrawable = if (defaultFolderIconRadioButton.isChecked)  // Use the default folder icon.
+            defaultFolderIconImageView.drawable
+        else if (webpageFavoriteIconRadioButton.isChecked)  // Use the webpage favorite icon.
+            webpageFavoriteIconImageView.drawable
+        else  // Use the custom icon.
+            customIconImageView.drawable
+
+        // Cast the folder icon bitmap to a bitmap drawable.
+        val folderIconBitmapDrawable = folderIconDrawable as BitmapDrawable
+
+        // Convert the folder icon bitmap drawable to a bitmap.
+        val folderIconBitmap = folderIconBitmapDrawable.bitmap
 
         // Create a folder icon byte array output stream.
         val folderIconByteArrayOutputStream = ByteArrayOutputStream()
@@ -3989,11 +3976,11 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             val databaseId = bookmarksListView.getItemIdAtPosition(i).toInt()
 
             // Move the bookmark down one slot.
-            bookmarksDatabaseHelper!!.updateDisplayOrder(databaseId, i + 1)
+            bookmarksDatabaseHelper!!.updateDisplayOrder(databaseId, displayOrder = i + 1)
         }
 
         // Create the folder, which will be placed at the top of the list view.
-        bookmarksDatabaseHelper!!.createFolder(folderNameString, currentBookmarksFolderId, folderIconByteArray)
+        bookmarksDatabaseHelper!!.createFolder(folderNameString, currentBookmarksFolderId, displayOrder = 0, folderIconByteArray)
 
         // Update the bookmarks cursor with the current contents of this folder.
         bookmarksCursor = bookmarksDatabaseHelper!!.getBookmarksByDisplayOrder(currentBookmarksFolderId)
@@ -4005,20 +3992,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         bookmarksListView.setSelection(0)
     }
 
-    private fun downloadUrlWithExternalApp(url: String) {
-        // Create a download intent.  Not specifying the action type will display the maximum number of options.
-        val downloadIntent = Intent()
-
-        // Set the URI and the mime type.
-        downloadIntent.setDataAndType(Uri.parse(url), "text/html")
-
-        // Flag the intent to open in a new task.
-        downloadIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
-
-        // Show the chooser.
-        startActivity(Intent.createChooser(downloadIntent, getString(R.string.download_with_external_app)))
-    }
-
     private fun exitFullScreenVideo() {
         // Re-enable the screen timeout.
         fullScreenVideoFrameLayout.keepScreenOn = false
@@ -4090,7 +4063,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         // Check to see if the activity has been restarted with a saved state.
         if ((savedStateArrayList == null) || (savedStateArrayList!!.size == 0)) {  // The activity has not been restarted or it was restarted on start to change the theme.
             // Add the first tab.
-            addNewTab("", false)
+            addNewPage(urlString = "", adjacent = false, moveToTab = false)
         } else {  // The activity has been restarted with a saved state.
             // Restore each tab.
             for (i in savedStateArrayList!!.indices) {
@@ -4149,7 +4122,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     loadingNewIntent = true
 
                     // Add a new tab.
-                    addNewTab(urlString, true)
+                    addNewPage(urlString, adjacent = false, moveToTab = true)
                 } else {  // Load the URL in the current tab.
                     // Make it so.
                     loadUrl(currentWebView!!, urlString)
@@ -4177,7 +4150,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     @SuppressLint("ClickableViewAccessibility")
     private fun initializeApp() {
         // Get a handle for the input method.
-        val inputMethodManager = (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager)
+        inputMethodManager = (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager)
 
         // Initialize the color spans for highlighting the URLs.
         initialGrayColorSpan = ForegroundColorSpan(getColor(R.color.gray_500))
@@ -4294,8 +4267,10 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     // Select the same page in the view pager.
                     webViewViewPager2.currentItem = tab.position
 
-                    // Set the current WebView.
-                    setCurrentWebView(tab.position)
+                    // Set the current WebView after the tab layout has quiesced (otherwise, sometimes the wong WebView might be used).  See <https://redmine.stoutner.com/issues/1136>
+                    tabLayout.post {
+                        setCurrentWebView(tab.position)
+                    }
                 }
             }
 
@@ -4364,11 +4339,15 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
 
         // Search for the string on the page whenever a character changes in the find on page edit text.
         findOnPageEditText.addTextChangedListener(object : TextWatcher {
-            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
+            override fun beforeTextChanged(charSequence: CharSequence, start: Int, count: Int, after: Int) {
+                // Do nothing.
+            }
 
-            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
+            override fun onTextChanged(charSequence: CharSequence, start: Int, before: Int, count: Int) {
+                // Do nothing.
+            }
 
-            override fun afterTextChanged(s: Editable) {
+            override fun afterTextChanged(editable: Editable) {
                 // Search for the text in the WebView if it is not null.  Sometimes on resume after a period of non-use the WebView will be null.
                 currentWebView?.findAllAsync(findOnPageEditText.text.toString())
             }
@@ -4470,7 +4449,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Open each bookmark
                 for (i in 0 until bookmarksCursor.count) {
                     // Load the bookmark in a new tab, moving to the tab for the first bookmark if the drawer is not pinned.
-                    addNewTab(bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BOOKMARK_URL)), !bookmarksDrawerPinned && (i == 0))
+                    addNewPage(bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BOOKMARK_URL)), adjacent = false, moveToTab = !bookmarksDrawerPinned && (i == 0))
 
                     // Move to the next bookmark.
                     bookmarksCursor.moveToNext()
@@ -4486,7 +4465,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 bookmarkCursor.moveToFirst()
 
                 // Load the bookmark in a new tab and move to the tab if the drawer is not pinned.
-                addNewTab(bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BOOKMARK_URL)), !bookmarksDrawerPinned)
+                addNewPage(bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BOOKMARK_URL)), adjacent = true, moveToTab = !bookmarksDrawerPinned)
 
                 // Close the cursor.
                 bookmarkCursor.close()
@@ -4506,10 +4485,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
 
             override fun onDrawerOpened(drawerView: View) {}
 
-            override fun onDrawerClosed(drawerView: View) {
-                // Reset the drawer icon when the drawer is closed.  Otherwise, it remains an arrow if the drawer is open when the app is restarted.
-                actionBarDrawerToggle!!.syncState()
-            }
+            override fun onDrawerClosed(drawerView: View) {}
 
             override fun onDrawerStateChanged(newState: Int) {
                 if (newState == DrawerLayout.STATE_SETTLING || newState == DrawerLayout.STATE_DRAGGING) {  // A drawer is opening or closing.
@@ -4587,7 +4563,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     }
 
     @SuppressLint("ClickableViewAccessibility")
-    override fun initializeWebView(nestedScrollWebView: NestedScrollWebView, pageNumber: Int, progressBar: ProgressBar, urlString: String, restoringState: Boolean) {
+    override fun initializeWebView(nestedScrollWebView: NestedScrollWebView, pagePosition: Int, progressBar: ProgressBar, urlString: String, restoringState: Boolean) {
         // Get the WebView theme.
         val webViewTheme = sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value))
 
@@ -4626,9 +4602,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             }
         }
 
-        // Get a handle for the input method manager.
-        val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
-
         // Set the app bar scrolling.
         nestedScrollWebView.isNestedScrollingEnabled = scrollAppBar
 
@@ -4714,8 +4687,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
 
                         // The deprecated command can be switched to `WindowInsetsController` once the minimum API >= 30.
                         @Suppress("DEPRECATION")
-                        rootFrameLayout.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
-                                View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+                        rootFrameLayout.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                     } else {  // Switch to normal viewing mode.
                         // Show the app bar if it was hidden.
                         if (hideAppBar) {
@@ -4803,13 +4775,12 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         registerForContextMenu(nestedScrollWebView)
 
         // Allow the downloading of files.
-        nestedScrollWebView.setDownloadListener { downloadUrlString: String?, userAgent: String?, contentDisposition: String?, mimetype: String?, contentLength: Long ->
-            // Check the download preference.
+        nestedScrollWebView.setDownloadListener { downloadUrlString: String, userAgent: String, contentDisposition: String, mimetype: String, contentLength: Long ->
+            // Use the specified download provider.
             if (downloadWithExternalApp) {  // Download with an external app.
-                downloadUrlWithExternalApp(downloadUrlString!!)
-            } else {  // Handle the download inside of Privacy Browser.
-                // Define a formatted file size string.
-
+                // Download with an external app.
+                saveWithExternalApp(downloadUrlString)
+            } else {  // Download with Privacy Browser or Android's download manager.
                 // Process the content length if it contains data.
                 val formattedFileSizeString = if (contentLength > 0) {  // The content length is greater than 0.
                     // Format the content length as a string.
@@ -4820,10 +4791,10 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 }
 
                 // Get the file name from the content disposition.
-                val fileNameString = UrlHelper.getFileName(this, contentDisposition, mimetype, downloadUrlString!!)
+                val fileNameString = UrlHelper.getFileName(this, contentDisposition, mimetype, downloadUrlString)
 
-                // Instantiate the save dialog.
-                val saveDialogFragment = SaveDialog.saveUrl(downloadUrlString, fileNameString, formattedFileSizeString, userAgent!!, nestedScrollWebView.acceptCookies)
+                // Instantiate the save dialog according.
+                val saveDialogFragment = SaveDialog.saveUrl(downloadUrlString, fileNameString, formattedFileSizeString, userAgent, nestedScrollWebView.acceptCookies)
 
                 // Try to show the dialog.  The download listener continues to function even when the WebView is paused.  Attempting to display a dialog in that state leads to a crash.
                 try {
@@ -4834,6 +4805,29 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     pendingDialogsArrayList.add(PendingDialogDataClass(saveDialogFragment, getString(R.string.save_dialog)))
                 }
             }
+
+            // Get the current page position.
+            val currentPagePosition = webViewStateAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
+
+            // Get the corresponding tab.
+            val tab = tabLayout.getTabAt(currentPagePosition)!!
+
+            // Get the tab custom view.
+            val tabCustomView = tab.customView!!
+
+            // Get the tab views.
+            val tabFavoriteIconImageView = tabCustomView.findViewById<ImageView>(R.id.favorite_icon_imageview)
+            val tabTitleTextView = tabCustomView.findViewById<TextView>(R.id.title_textview)
+
+            // Restore the previous webpage favorite icon and title if the title is currently set to `Loading...`.
+            if (tabTitleTextView.text.toString() == getString(R.string.loading)) {
+                // Restore the previous webpage title text.
+                tabTitleTextView.text = nestedScrollWebView.previousWebpageTitle
+
+                // Restore the previous webpage favorite icon if it is not null.
+                if (nestedScrollWebView.previousFavoriteIconDrawable != null)
+                    tabFavoriteIconImageView.setImageDrawable(nestedScrollWebView.previousFavoriteIconDrawable)
+            }
         }
 
         // Update the find on page count.
@@ -4926,7 +4920,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                             val tabFavoriteIconImageView = tabView.findViewById<ImageView>(R.id.favorite_icon_imageview)
 
                             // Display the favorite icon in the tab.
-                            tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(icon, 64, 64, true))
+                            tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(icon, 128, 128, true))
                         }
                     }
                 }
@@ -5457,8 +5451,14 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Instantiate an HTTP authentication dialog.
                 val httpAuthenticationDialogFragment = HttpAuthenticationDialog.displayDialog(host, realm, nestedScrollWebView.webViewFragmentId)
 
-                // Show the HTTP authentication dialog.
-                httpAuthenticationDialogFragment.show(supportFragmentManager, getString(R.string.http_authentication))
+                // Try to show the dialog.  WebView can receive an HTTP authentication request even after the app has been paused.  Attempting to display a dialog in that state leads to a crash.
+                try {
+                    // Show the HTTP authentication dialog.
+                    httpAuthenticationDialogFragment.show(supportFragmentManager, getString(R.string.http_authentication))
+                } catch (exception: Exception) {  // The dialog could not be shown.
+                    // Add the dialog to the pending dialog array list.  It will be displayed in `onStart()`.
+                    pendingDialogsArrayList.add(PendingDialogDataClass(httpAuthenticationDialogFragment, getString(R.string.http_authentication)))
+                }
             }
 
             override fun onPageStarted(webView: WebView, url: String, favicon: Bitmap?) {
@@ -5696,7 +5696,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     // Instantiate an SSL certificate error alert dialog.
                     val sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error, nestedScrollWebView.webViewFragmentId)
 
-                    // Try to show the dialog.  The SSL error handler continues to function even when the WebView is paused.  Attempting to display a dialog in that state leads to a crash.
+                    // Try to show the dialog.  The SSL error handler continues to function even when the app has been stopped.  Attempting to display a dialog in that state leads to a crash.
                     try {
                         // Show the SSL certificate error dialog.
                         sslCertificateErrorDialogFragment.show(supportFragmentManager, getString(R.string.ssl_certificate_error))
@@ -5712,7 +5712,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         if (restoringState) {  // The state is being restored.
             // Resume the nested scroll WebView JavaScript timers.
             nestedScrollWebView.resumeTimers()
-        } else if (pageNumber == 0) {  // The first page is being loaded.
+        } else if (pagePosition == 0) {  // The first page is being loaded.
             // Set this nested scroll WebView as the current WebView.
             currentWebView = nestedScrollWebView
 
@@ -5767,17 +5767,10 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Request focus for the URL text box.
                 urlEditText.requestFocus()
 
-                // Create a display keyboard handler.
-                val displayKeyboardHandler = Handler(Looper.getMainLooper())
-
-                // Create a display keyboard runnable.
-                val displayKeyboardRunnable = Runnable {
-                    // Display the keyboard.
+                // Display the keyboard once the tab layout has settled.
+                tabLayout.post {
                     inputMethodManager.showSoftInput(urlEditText, 0)
                 }
-
-                // Display the keyboard after 100 milliseconds, which leaves enough time for the tab to transition.
-                displayKeyboardHandler.postDelayed(displayKeyboardRunnable, 100)
             }
         }
     }
@@ -5902,15 +5895,56 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         loadUrl(currentWebView!!, urlString)
     }
 
-    override fun navigateHistory(url: String, steps: Int) {
+    override fun navigateHistory(steps: Int) {
+        // Get the current web back forward list.
+        val webBackForwardList = currentWebView!!.copyBackForwardList()
+
+        // Calculate the target index.
+        val targetIndex = webBackForwardList.currentIndex + steps
+
+        // Get the previous entry data.
+        val previousUrl = webBackForwardList.getItemAtIndex(targetIndex).url
+        val previousFavoriteIcon = webBackForwardList.getItemAtIndex(targetIndex).favicon
+
         // Apply the domain settings.
-        applyDomainSettings(currentWebView!!, url, resetTab = false, reloadWebsite = false, loadUrl = false)
+        applyDomainSettings(currentWebView!!, previousUrl, resetTab = false, reloadWebsite = false, loadUrl = false)
+
+        // Get the current tab.
+        val tab = tabLayout.getTabAt(tabLayout.selectedTabPosition)!!
+
+        // Get the custom view from the tab.
+        val tabView = tab.customView!!
+
+        // Get the favorite icon image view from the tab.
+        val tabFavoriteIconImageView = tabView.findViewById<ImageView>(R.id.favorite_icon_imageview)
+
+        // Store the previous favorite icon.
+        if (previousFavoriteIcon == null)
+            currentWebView!!.setFavoriteIcon(defaultFavoriteIconBitmap)
+        else
+            currentWebView!!.setFavoriteIcon(previousFavoriteIcon)
+
+        // Display the previous favorite icon in the tab.
+        tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(currentWebView!!.getFavoriteIcon(), 128, 128, true))
 
         // Load the history entry.
         currentWebView!!.goBackOrForward(steps)
 
-        // Update the URL edit text after a delay.
-        updateUrlEditTextAfterDelay()
+        // Create a handler to update the URL edit box.
+        val urlEditTextUpdateHandler = Handler(Looper.getMainLooper())
+
+        // Create a runnable to update the URL edit box.
+        val urlEditTextUpdateRunnable = Runnable {
+            // Update the URL edit text.
+            urlEditText.setText(currentWebView!!.url)
+
+            // Disable the wide viewport if the source is being viewed.
+            if (currentWebView!!.url!!.startsWith("view-source:"))
+                currentWebView!!.settings.useWideViewPort = false
+        }
+
+        // Update the URL edit text after 50 milliseconds, so that the WebView has enough time to navigate to the new URL.
+        urlEditTextUpdateHandler.postDelayed(urlEditTextUpdateRunnable, 50)
     }
 
     override fun openFile(dialogFragment: DialogFragment) {
@@ -5967,6 +6001,11 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             currentWebView!!.loadUrl(openFilePath)
         }
     }
+    // The view parameter cannot be removed because it is called from the layout onClick.
+    fun openNavigationDrawer(@Suppress("UNUSED_PARAMETER")view: View) {
+        // Open the navigation drawer.
+        drawerLayout.openDrawer(GravityCompat.START)
+    }
 
     private fun openWithApp(url: String) {
         // Create an open with app intent with `ACTION_VIEW`.
@@ -6010,20 +6049,8 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     }
 
     override fun pinnedErrorGoBack() {
-        // Get the current web back forward list.
-        val webBackForwardList = currentWebView!!.copyBackForwardList()
-
-        // Get the previous entry URL.
-        val previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.currentIndex - 1).url
-
-        // Apply the domain settings.
-        applyDomainSettings(currentWebView!!, previousUrl, resetTab = false, reloadWebsite = false, loadUrl = false)
-
-        // Go back.
-        currentWebView!!.goBack()
-
-        // Update the URL edit text after a delay.
-        updateUrlEditTextAfterDelay()
+        // Navigate back one page.
+        navigateHistory(-1)
     }
 
     private fun sanitizeUrl(urlString: String): String {
@@ -6042,7 +6069,76 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         return sanitizedUrlString
     }
 
-    override fun saveUrl(originalUrlString: String, fileNameString: String, dialogFragment: DialogFragment) {
+    override fun saveWithAndroidDownloadManager(dialogFragment: DialogFragment) {
+        // Get the dialog.
+        val dialog = dialogFragment.dialog!!
+
+        // Get handles for the dialog views.
+        val dialogUrlEditText = dialog.findViewById<EditText>(R.id.url_edittext)
+        val downloadDirectoryRadioGroup = dialog.findViewById<RadioGroup>(R.id.download_directory_radiogroup)
+        val dialogFileNameEditText = dialog.findViewById<EditText>(R.id.file_name_edittext)
+
+        // Get the string from the edit texts, which may have been modified by the user.
+        val saveUrlString = dialogUrlEditText.text.toString()
+        val fileNameString = dialogFileNameEditText.text.toString()
+
+        // Get a handle for the system download service.
+        val downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
+
+        // Parse the URL.
+        val downloadRequest = DownloadManager.Request(Uri.parse(saveUrlString))
+
+        // Pass cookies to download manager if cookies are enabled.  This is required to download files from websites that require a login.
+        // Code contributed 2017 Hendrik Knackstedt.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+        if (cookieManager.acceptCookie()) {
+            // Get the cookies for the URL.
+            val cookiesString = cookieManager.getCookie(saveUrlString)
+
+            // Add the cookies to the download request.  In the HTTP request header, cookies are named `Cookie`.
+            downloadRequest.addRequestHeader("Cookie", cookiesString)
+        }
+
+        // Get the download directory.
+        val downloadDirectory = when (downloadDirectoryRadioGroup.checkedRadioButtonId) {
+            R.id.downloads_radiobutton -> Environment.DIRECTORY_DOWNLOADS
+            R.id.documents_radiobutton -> Environment.DIRECTORY_DOCUMENTS
+            R.id.pictures_radiobutton -> Environment.DIRECTORY_PICTURES
+            else -> Environment.DIRECTORY_MUSIC
+        }
+
+        // Set the download destination.
+        downloadRequest.setDestinationInExternalPublicDir(downloadDirectory, fileNameString)
+
+        // Allow media scanner to index the download if it is a media file.  This is automatic for API >= 29.
+        @Suppress("DEPRECATION")
+        if (Build.VERSION.SDK_INT <= 28)
+            downloadRequest.allowScanningByMediaScanner()
+
+        // Add the URL as the description for the download.
+        downloadRequest.setDescription(saveUrlString)
+
+        // Show the download notification after the download is completed.
+        downloadRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
+
+        // Initiate the download.
+        downloadManager.enqueue(downloadRequest)
+    }
+
+    private fun saveWithExternalApp(url: String) {
+        // Create a download intent.  Not specifying the action type will display the maximum number of options.
+        val downloadIntent = Intent()
+
+        // Set the URI and the mime type.
+        downloadIntent.setDataAndType(Uri.parse(url), "text/html")
+
+        // Flag the intent to open in a new task.
+        downloadIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+
+        // Show the chooser.
+        startActivity(Intent.createChooser(downloadIntent, getString(R.string.download_with_external_app)))
+    }
+
+    override fun saveWithPrivacyBrowser(originalUrlString: String, fileNameString: String, dialogFragment: DialogFragment) {
         // Store the URL.  This will be used in the save URL activity result launcher.
         saveUrlString = if (originalUrlString.startsWith("data:")) {
             // Save the original URL.
@@ -6054,7 +6150,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             // Get a handle for the dialog URL edit text.
             val dialogUrlEditText = dialog.findViewById<EditText>(R.id.url_edittext)
 
-            // Get the URL from the edit text, which may have been modified.
+            // Get the URL from the edit text, which may have been modified by the user.
             dialogUrlEditText.text.toString()
         }
 
@@ -6092,9 +6188,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             // Update the privacy icons.  `true` redraws the icons in the app bar.
             updatePrivacyIcons(true)
 
-            // Get a handle for the input method manager.
-            val inputMethodManager = (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager)
-
             // Get the current URL.
             val urlString = currentWebView!!.url
 
@@ -6135,8 +6228,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Remove any background on the URL relative layout.
                 urlRelativeLayout.background = AppCompatResources.getDrawable(this, R.color.transparent)
             }
-        }  catch (exception: Exception) {
-            //  Try again in 100 milliseconds if the WebView has not yet been populated.
+        }  catch (exception: Exception) {  //  Try again in 10 milliseconds if the WebView has not yet been populated.
             // Create a handler to set the current WebView.
             val setCurrentWebViewHandler = Handler(Looper.getMainLooper())
 
@@ -6146,8 +6238,8 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 setCurrentWebView(pageNumber)
             }
 
-            // Try setting the current WebView again after 50 milliseconds.
-            setCurrentWebViewHandler.postDelayed(setCurrentWebWebRunnable, 50)
+            // Try setting the current WebView again after 10 milliseconds.
+            setCurrentWebViewHandler.postDelayed(setCurrentWebWebRunnable, 10)
         }
     }
 
@@ -6243,22 +6335,4 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 invalidateOptionsMenu()
         }
     }
-
-    fun updateUrlEditTextAfterDelay() {
-        // Create a handler to update the URL edit box.
-        val urlEditTextUpdateHandler = Handler(Looper.getMainLooper())
-
-        // Create a runnable to update the URL edit box.
-        val urlEditTextUpdateRunnable = Runnable {
-            // Update the URL edit text.
-            urlEditText.setText(currentWebView!!.url)
-
-            // Disable the wide viewport if the source is being viewed.
-            if (currentWebView!!.url!!.startsWith("view-source:"))
-                currentWebView!!.settings.useWideViewPort = false
-        }
-
-        // Update the URL edit text after 50 milliseconds, so that the WebView has enough time to navigate to the new URL.
-        urlEditTextUpdateHandler.postDelayed(urlEditTextUpdateRunnable, 50)
-    }
 }