]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt
Fix some files not being displayed in the file picker. https://redmine.stoutner...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / MainWebViewActivity.kt
index ed6ecb36ac89fd847a21bde9a5c4a826bbaafe0f..a1d8ecc78220aefd7b6a310dbdb0f62b4785befe 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Download cookie code contributed 2017 Hendrik Knackstedt.  Copyright assigned to 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
  *
  * 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.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
 import android.widget.AdapterView
 import android.widget.ArrayAdapter
 import android.widget.CheckBox
@@ -87,6 +86,7 @@ import android.widget.LinearLayout
 import android.widget.ListView
 import android.widget.ProgressBar
 import android.widget.RadioButton
 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 android.widget.RelativeLayout
 import android.widget.TextView
 
@@ -151,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_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
 import com.stoutner.privacybrowser.helpers.ENABLE_JAVASCRIPT
 import com.stoutner.privacybrowser.helpers.ENABLE_ULTRAPRIVACY
 import com.stoutner.privacybrowser.helpers.ENABLED
@@ -251,6 +250,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
 
         // Declare the public static variables.
         lateinit var appBarLayout: AppBarLayout
 
         // Declare the public static variables.
         lateinit var appBarLayout: AppBarLayout
+        lateinit var defaultFavoriteIconBitmap : Bitmap
     }
 
     // Declare the class variables.
     }
 
     // Declare the class variables.
@@ -278,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 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
     private lateinit var navigationBackMenuItem: MenuItem
     private lateinit var navigationForwardMenuItem: MenuItem
     private lateinit var navigationHistoryMenuItem: MenuItem
@@ -289,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 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
     private lateinit var optionsCookiesMenuItem: MenuItem
     private lateinit var optionsDarkWebViewMenuItem: MenuItem
     private lateinit var optionsDisplayImagesMenuItem: MenuItem
@@ -307,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 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
     private lateinit var optionsSwipeToRefreshMenuItem: MenuItem
     private lateinit var optionsUltraListMenuItem: MenuItem
     private lateinit var optionsUltraPrivacyMenuItem: MenuItem
@@ -362,7 +361,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     private var defaultEasyPrivacy = true
     private var defaultFanboysAnnoyanceList = true
     private var defaultFanboysSocialBlockingList = true
     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
     private var defaultProgressViewEndOffset = 0
     private var defaultProgressViewStartOffset = 0
     private var defaultJavaScript = false
@@ -395,14 +393,14 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     private var ultraPrivacy: ArrayList<List<Array<String>>>? = null
     private var waitingForProxy = false
 
     private var ultraPrivacy: ArrayList<List<Array<String>>>? = null
     private var waitingForProxy = false
 
-    // Define the save webpage image activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
+    // Define the browse file upload activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
     private val browseFileUploadActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
         // Pass the file to the WebView.
         fileChooserCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(activityResult.resultCode, activityResult.data))
     }
 
     // Define the save URL activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
     private val browseFileUploadActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
         // Pass the file to the WebView.
         fileChooserCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(activityResult.resultCode, activityResult.data))
     }
 
     // Define the save URL activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
-    private val saveUrlActivityResultLauncher = registerForActivityResult<String, Uri>(ActivityResultContracts.CreateDocument("*/*")) { fileUri ->
+    private val saveUrlActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("*/*")) { fileUri ->
         // Only save the URL if the file URI is not null, which happens if the user exited the file picker by pressing back.
         if (fileUri != null) {
             // Instantiate the save URL coroutine.
         // Only save the URL if the file URI is not null, which happens if the user exited the file picker by pressing back.
         if (fileUri != null) {
             // Instantiate the save URL coroutine.
@@ -417,26 +415,20 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     }
 
     // Define the save webpage archive activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
     }
 
     // Define the save webpage archive activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
-    private val saveWebpageArchiveActivityResultLauncher = registerForActivityResult<String, Uri>(ActivityResultContracts.CreateDocument("multipart/related")) { fileUri ->
+    private val saveWebpageArchiveActivityResultLauncher = registerForActivityResult(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) {
         // 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
-
-            // 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)!!
+            // 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.
-                fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
+            // Get the file name from the cursor.
+            val 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 {
 
             // Use a coroutine to save the file.
             CoroutineScope(Dispatchers.Main).launch {
@@ -496,7 +488,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     }
 
     // Define the save webpage image activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
     }
 
     // Define the save webpage image activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
-    private val saveWebpageImageActivityResultLauncher = registerForActivityResult<String, Uri>(ActivityResultContracts.CreateDocument("image/png")) { fileUri ->
+    private val saveWebpageImageActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("image/png")) { fileUri ->
         // Only save the webpage image if the file URI is not null, which happens if the user exited the file picker by pressing back.
         if (fileUri != null) {
             // Instantiate the save webpage image coroutine.
         // Only save the webpage image if the file URI is not null, which happens if the user exited the file picker by pressing back.
         if (fileUri != null) {
             // Instantiate the save webpage image coroutine.
@@ -532,7 +524,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
         }
 
                 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.
         val appThemeEntryValuesStringArray = resources.getStringArray(R.array.app_theme_entry_values)
 
         // Get the current theme status.
@@ -666,6 +658,15 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             // Update the bookmarks drawer pinned image view.
             updateBookmarksDrawerPinnedImageView()
 
             // 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()
 
             // Initialize the app.
             initializeApp()
 
@@ -687,34 +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.
                         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 data.
-                        val previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.currentIndex - 1).url
-                        val previousFavoriteIcon = webBackForwardList.getItemAtIndex(webBackForwardList.currentIndex - 1).favicon
-
-                        // Apply the domain settings.
-                        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)
-
-                        // Set the previous favorite icon if it isn't null.
-                        if (previousFavoriteIcon != null)
-                            tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(previousFavoriteIcon, 64, 64, true))
-
-                        // 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)
                     } else {  // Close the current tab.
                         // A view is required because the method is also called by an XML `onClick`.
                         closeTab(null)
@@ -919,9 +894,9 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         pendingDialogsArrayList.clear()
     }
 
         pendingDialogsArrayList.clear()
     }
 
-    public override fun onSaveInstanceState(savedInstanceState: Bundle) {
+    public override fun onSaveInstanceState(outState: Bundle) {
         // Run the default commands.
         // Run the default commands.
-        super.onSaveInstanceState(savedInstanceState)
+        super.onSaveInstanceState(outState)
 
         // Only save the instance state if the WebView state adapter is not null, which will be the case if the app is restarting to change the initial app theme.
         if (webViewStateAdapter != null) {
 
         // Only save the instance state if the WebView state adapter is not null, which will be the case if the app is restarting to change the initial app theme.
         if (webViewStateAdapter != null) {
@@ -959,11 +934,11 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             val currentTabPosition = tabLayout.selectedTabPosition
 
             // Store the saved states in the bundle.
             val currentTabPosition = tabLayout.selectedTabPosition
 
             // Store the saved states in the bundle.
-            savedInstanceState.putBoolean(BOOKMARKS_DRAWER_PINNED, bookmarksDrawerPinned)
-            savedInstanceState.putString(PROXY_MODE, proxyMode)
-            savedInstanceState.putParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST, savedNestedScrollWebViewStateArrayList)
-            savedInstanceState.putParcelableArrayList(SAVED_STATE_ARRAY_LIST, savedStateArrayList)
-            savedInstanceState.putInt(SAVED_TAB_POSITION, currentTabPosition)
+            outState.putBoolean(BOOKMARKS_DRAWER_PINNED, bookmarksDrawerPinned)
+            outState.putString(PROXY_MODE, proxyMode)
+            outState.putParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST, savedNestedScrollWebViewStateArrayList)
+            outState.putParcelableArrayList(SAVED_STATE_ARRAY_LIST, savedStateArrayList)
+            outState.putInt(SAVED_TAB_POSITION, currentTabPosition)
         }
     }
 
         }
     }
 
@@ -1048,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)
         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)
         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)
         optionsEasyListMenuItem = menu.findItem(R.id.easylist)
         optionsEasyPrivacyMenuItem = menu.findItem(R.id.easyprivacy)
         optionsFanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list)
@@ -1090,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)
 
         // 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)
         // 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)
@@ -1113,7 +1079,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             // Set the title.
             optionsRefreshMenuItem.setTitle(R.string.stop)
 
             // 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)
         }
             if (displayAdditionalAppBarIcons)
                 optionsRefreshMenuItem.setIcon(R.drawable.close_blue)
         }
@@ -1146,8 +1112,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
 
             // Set the status of the menu item checkboxes.
             optionsDomStorageMenuItem.isChecked = currentWebView!!.settings.domStorageEnabled
 
             // 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
             optionsEasyListMenuItem.isChecked = currentWebView!!.easyListEnabled
             optionsEasyPrivacyMenuItem.isChecked = currentWebView!!.easyPrivacyEnabled
             optionsFanboysAnnoyanceListMenuItem.isChecked = currentWebView!!.fanboysAnnoyanceListEnabled
@@ -1217,18 +1181,8 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         // Enable Clear DOM Storage if there is any.
         optionsClearDomStorageMenuItem.isEnabled = localStorageDirectoryNumberOfFiles > 0 || indexedDBDirectoryNumberOfFiles > 0
 
         // 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.
         // 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
 
         // Disable Fanboy's Social Blocking List menu item if Fanboy's Annoyance List is checked.
         optionsFanboysSocialBlockingListMenuItem.isEnabled = !optionsFanboysAnnoyanceListMenuItem.isChecked
@@ -1472,32 +1426,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 true
             }
 
                 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)
             R.id.clear_cookies -> {  // Clear cookies.
                 // Create a snackbar.
                 Snackbar.make(webViewViewPager2, R.string.cookies_deleted, Snackbar.LENGTH_LONG)
@@ -1573,28 +1501,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 true
             }
 
                 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
             R.id.easylist -> {  // EasyList.
                 // Toggle the EasyList status.
                 currentWebView!!.easyListEnabled = !currentWebView!!.easyListEnabled
@@ -1948,9 +1854,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     // Set the focus on the find on page edit text.
                     findOnPageEditText.requestFocus()
 
                     // 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)
                     // Display the keyboard.  `0` sets no input flags.
                     inputMethodManager.showSoftInput(findOnPageEditText, 0)
                 }, 200)
@@ -1976,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.
             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)
 
                 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)
 
@@ -2114,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)
                     // 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.
                     domainsIntent.putExtra(CURRENT_IP_ADDRESSES, currentWebView!!.currentIpAddresses)
 
                     // Get the current certificate.
@@ -2167,19 +2069,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     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))
 
                     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))
 
-                    // Initialize the form data int.
-                    var formDataInt = SYSTEM_DEFAULT
-
-                    // Set the form data int, which can be removed once the minimum API >= 26.
-                    @Suppress("DEPRECATION")
-                    if (Build.VERSION.SDK_INT < 26) {
-                        // Get the form data status.
-                        val formDataEnabled = currentWebView!!.settings.saveFormData
-
-                        // Calculate the form data Int.
-                        formDataInt = calculateSettingsInt(formDataEnabled, sharedPreferences.getBoolean(getString(R.string.save_form_data_key), false))
-                    }
-
                     // Get the current user agent string.
                     val currentUserAgentString = currentWebView!!.settings.userAgentString
 
                     // Get the current user agent string.
                     val currentUserAgentString = currentWebView!!.settings.userAgentString
 
@@ -2261,7 +2150,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                         LIGHT_THEME
 
                     // Create the domain and store the database ID.
                         LIGHT_THEME
 
                     // Create the domain and store the database ID.
-                    val newDomainDatabaseId = domainsDatabaseHelper!!.addDomain(currentDomain, javaScriptInt, cookiesInt, domStorageInt, formDataInt, userAgentName, easyListInt, easyPrivacyInt,
+                    val newDomainDatabaseId = domainsDatabaseHelper!!.addDomain(currentDomain, javaScriptInt, cookiesInt, domStorageInt, userAgentName, easyListInt, easyPrivacyInt,
                                                                                 fanboysAnnoyanceListInt, fanboysSocialBlockingListInt, ultraListInt, ultraPrivacyInt, blockAllThirdPartyRequestsInt, fontSizeInt,
                                                                                 swipeToRefreshInt, webViewThemeInt, wideViewportInt, displayImagesInt)
 
                                                                                 fanboysAnnoyanceListInt, fanboysSocialBlockingListInt, ultraListInt, ultraPrivacyInt, blockAllThirdPartyRequestsInt, fontSizeInt,
                                                                                 swipeToRefreshInt, webViewThemeInt, wideViewportInt, displayImagesInt)
 
@@ -2271,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)
                     // 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.
                     domainsIntent.putExtra(CURRENT_IP_ADDRESSES, currentWebView!!.currentIpAddresses)
 
                     // Get the current certificate.
@@ -2331,66 +2219,16 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             R.id.back -> {  // Back.
                 // Check if the WebView can go back.
                 if (currentWebView!!.canGoBack()) {
             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 data.
-                    val previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.currentIndex - 1).url
-                    val previousFavoriteIcon = webBackForwardList.getItemAtIndex(webBackForwardList.currentIndex - 1).favicon!!
-
-                    // Apply the domain settings.
-                    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)
-
-                    // Set the previous favorite icon.
-                    tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(previousFavoriteIcon, 64, 64, true))
-
-                    // 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()) {
                 }
             }
 
             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 data.
-                    val nextUrl = webBackForwardList.getItemAtIndex(webBackForwardList.currentIndex + 1).url
-                    val nextFavoriteIcon = webBackForwardList.getItemAtIndex(webBackForwardList.currentIndex + 1).favicon!!
-
-                    // Apply the domain settings.
-                    applyDomainSettings(currentWebView!!, nextUrl, 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)
-
-                    // Set the next favorite icon.
-                    tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(nextFavoriteIcon, 64, 64, true))
-
-                    // Load the next website in the history.
-                    currentWebView!!.goForward()
-
-                    // Update the URL edit text after a delay.
-                    updateUrlEditTextAfterDelay()
+                    // Navigate forward one page.
+                    navigateHistory(+1)
                 }
             }
 
                 }
             }
 
@@ -2488,7 +2326,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 val domainsIntent = Intent(this, DomainsActivity::class.java)
 
                 // Add the extra information to the intent.
                 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.
                 domainsIntent.putExtra(CURRENT_IP_ADDRESSES, currentWebView!!.currentIpAddresses)
 
                 // Get the current certificate.
@@ -2663,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.
                 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)
 
                     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)
 
@@ -2747,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.
                 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)
                     }
                     } 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)
                     }
@@ -2871,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.
                 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)
 
                     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)
 
@@ -2916,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.
                 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)
 
                     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)
 
@@ -3074,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)
         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)
         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)
@@ -3089,12 +2925,11 @@ 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)
 
         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)
         webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values)
-
-        // Get the user agent string arrays.  These are done here so that expensive resource requests are not made each time a domain is loaded.
         userAgentDataArray = resources.getStringArray(R.array.user_agent_data)
         userAgentNamesArray = resources.getStringArray(R.array.user_agent_names)
         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)
 
         // 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)
@@ -3107,9 +2942,12 @@ 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)
         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)
+        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)
 
         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) {
             // Apply the saved proxy mode.
         // Apply the saved proxy mode if the app has been restarted.
         if (savedProxyMode != null) {
             // Apply the saved proxy mode.
@@ -3265,10 +3103,10 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             nestedScrollWebView.clearPinnedSslCertificate()
             nestedScrollWebView.pinnedIpAddresses = ""
 
             nestedScrollWebView.clearPinnedSslCertificate()
             nestedScrollWebView.pinnedIpAddresses = ""
 
-            // Reset the favorite icon if specified.
+            // Reset the tab if specified.
             if (resetTab) {
                 // Initialize the favorite icon.
             if (resetTab) {
                 // Initialize the favorite icon.
-                nestedScrollWebView.initializeFavoriteIcon()
+                nestedScrollWebView.resetFavoriteIcon()
 
                 // Get the current page position.
                 val currentPagePosition = webViewStateAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
 
                 // Get the current page position.
                 val currentPagePosition = webViewStateAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
@@ -3290,7 +3128,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     nestedScrollWebView.previousWebpageTitle = tabTitleTextView.text.toString()
 
                     // Set the default favorite icon as the favorite icon for this tab.
                     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)
 
                     // Set the loading title text.
                     tabTitleTextView.setText(R.string.loading)
@@ -3339,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 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))
                 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))
@@ -3392,17 +3229,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     DISABLED -> nestedScrollWebView.settings.domStorageEnabled = false
                 }
 
                     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
                 // Set the EasyList status.
                 when (easyListInt) {
                     SYSTEM_DEFAULT -> nestedScrollWebView.easyListEnabled = defaultEasyList
@@ -3607,11 +3433,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Apply the default cookie setting.
                 cookieManager.setAcceptCookie(nestedScrollWebView.acceptCookies)
 
                 // 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.
                 // Apply the default font size setting.
                 try {
                     // Try to set the font size from the value in the app settings.
@@ -3922,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 {
         // Clear the logcat.
         if (clearEverything || sharedPreferences.getBoolean(getString(R.string.clear_logcat_key), true)) {
             try {
@@ -4056,9 +3857,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         // Show the toolbar.
         toolbar.visibility = View.VISIBLE
 
         // 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)
     }
         // Hide the keyboard.
         inputMethodManager.hideSoftInputFromWindow(toolbar.windowToken, 0)
     }
@@ -4082,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.
         // 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.
 
         // 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()
 
         // Create a favorite icon byte array output stream.
         val favoriteIconByteArrayOutputStream = ByteArrayOutputStream()
@@ -4119,32 +3932,34 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         bookmarksListView.setSelection(newBookmarkDisplayOrder)
     }
 
         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.
         // 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 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.
 
         // 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()
 
         // Create a folder icon byte array output stream.
         val folderIconByteArrayOutputStream = ByteArrayOutputStream()
@@ -4177,20 +3992,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         bookmarksListView.setSelection(0)
     }
 
         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
     private fun exitFullScreenVideo() {
         // Re-enable the screen timeout.
         fullScreenVideoFrameLayout.keepScreenOn = false
@@ -4349,7 +4150,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     @SuppressLint("ClickableViewAccessibility")
     private fun initializeApp() {
         // Get a handle for the input method.
     @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))
 
         // Initialize the color spans for highlighting the URLs.
         initialGrayColorSpan = ForegroundColorSpan(getColor(R.color.gray_500))
@@ -4538,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 {
 
         // 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())
             }
                 // 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())
             }
@@ -4797,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
 
         // Set the app bar scrolling.
         nestedScrollWebView.isNestedScrollingEnabled = scrollAppBar
 
@@ -4973,13 +4775,12 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         registerForContextMenu(nestedScrollWebView)
 
         // Allow the downloading of files.
         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.
             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.
                 // 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.
@@ -4990,10 +4791,10 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 }
 
                 // Get the file name from the content disposition.
                 }
 
                 // 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 {
 
                 // 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 {
@@ -5119,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.
                             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))
                         }
                     }
                 }
                         }
                     }
                 }
@@ -5201,26 +5002,17 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Store the file path callback.
                 fileChooserCallback = filePathCallback
 
                 // Store the file path callback.
                 fileChooserCallback = filePathCallback
 
-                // Create an intent to open a chooser based on the file chooser parameters.
-                val fileChooserIntent = fileChooserParams.createIntent()
+                // Create a file chooser intent.
+                val fileChooserIntent = Intent(Intent.ACTION_GET_CONTENT)
 
 
-                // Check to see if the file chooser intent resolves to an installed package.
-                if (fileChooserIntent.resolveActivity(packageManager) != null) {  // The file chooser intent is fine.
-                    // Launch the file chooser intent.
-                    browseFileUploadActivityResultLauncher.launch(fileChooserIntent)
-                } else {  // The file chooser intent will cause a crash.
-                    // Create a generic intent to open a chooser.
-                    val genericFileChooserIntent = Intent(Intent.ACTION_GET_CONTENT)
+                // Request an openable file.
+                fileChooserIntent.addCategory(Intent.CATEGORY_OPENABLE)
 
 
-                    // Request an openable file.
-                    genericFileChooserIntent.addCategory(Intent.CATEGORY_OPENABLE)
+                // Set the file type to everything.  The file chooser params cannot be used to create the intent because it only selects the first specified file type.  See <https://redmine.stoutner.com/issues/1197>.
+                fileChooserIntent.type = "*/*"
 
 
-                    // Set the file type to everything.
-                    genericFileChooserIntent.type = "*/*"
-
-                    // Launch the generic file chooser intent.
-                    browseFileUploadActivityResultLauncher.launch(genericFileChooserIntent)
-                }
+                // Launch the file chooser intent.
+                browseFileUploadActivityResultLauncher.launch(fileChooserIntent)
 
                 // Handle the event.
                 return true
 
                 // Handle the event.
                 return true
@@ -5650,8 +5442,14 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 // Instantiate an HTTP authentication dialog.
                 val httpAuthenticationDialogFragment = HttpAuthenticationDialog.displayDialog(host, realm, nestedScrollWebView.webViewFragmentId)
 
                 // 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?) {
             }
 
             override fun onPageStarted(webView: WebView, url: String, favicon: Bitmap?) {
@@ -5889,7 +5687,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                     // Instantiate an SSL certificate error alert dialog.
                     val sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error, nestedScrollWebView.webViewFragmentId)
 
                     // 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))
                     try {
                         // Show the SSL certificate error dialog.
                         sslCertificateErrorDialogFragment.show(supportFragmentManager, getString(R.string.ssl_certificate_error))
@@ -6088,15 +5886,56 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         loadUrl(currentWebView!!, urlString)
     }
 
         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.
         // 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)
 
 
         // 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) {
     }
 
     override fun openFile(dialogFragment: DialogFragment) {
@@ -6201,20 +6040,8 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     }
 
     override fun pinnedErrorGoBack() {
     }
 
     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 {
     }
 
     private fun sanitizeUrl(urlString: String): String {
@@ -6233,7 +6060,76 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
         return sanitizedUrlString
     }
 
         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.
         // Store the URL.  This will be used in the save URL activity result launcher.
         saveUrlString = if (originalUrlString.startsWith("data:")) {
             // Save the original URL.
@@ -6245,7 +6141,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 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()
         }
 
             dialogUrlEditText.text.toString()
         }
 
@@ -6283,9 +6179,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             // Update the privacy icons.  `true` redraws the icons in the app bar.
             updatePrivacyIcons(true)
 
             // 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
 
             // Get the current URL.
             val urlString = currentWebView!!.url
 
@@ -6433,22 +6326,4 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 invalidateOptionsMenu()
         }
     }
                 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)
-    }
 }
 }