X-Git-Url: https://gitweb.stoutner.com/?a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FMainWebViewActivity.kt;h=042af73dce4f9258440b6da318dcdbfaac7ba25d;hb=refs%2Fheads%2Fmaster;hp=f43e66d1fc74281202731d91dcc9b829a3d4a0f4;hpb=f196ab85ff8aaab92fb32a4a310a91921ab8fb83;p=PrivacyBrowserAndroid.git diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt index f43e66d1..042af73d 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt @@ -1,9 +1,9 @@ /* - * Copyright 2015-2023 Soren Stoutner . + * Copyright 2015-2024 Soren Stoutner . * * Download cookie code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner . * - * This file is part of Privacy Browser Android . + * This file is part of Privacy Browser Android . * * Privacy Browser Android is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -76,7 +76,6 @@ import android.webkit.WebSettings import android.webkit.WebStorage import android.webkit.WebView import android.webkit.WebViewClient -import android.webkit.WebViewDatabase import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.CheckBox @@ -87,6 +86,7 @@ import android.widget.LinearLayout import android.widget.ListView import android.widget.ProgressBar import android.widget.RadioButton +import android.widget.RadioGroup import android.widget.RelativeLayout import android.widget.TextView @@ -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_FORM_DATA 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 + lateinit var defaultFavoriteIconBitmap : Bitmap } // 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 inputMethodManager: InputMethodManager 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 optionsClearFormDataMenuItem: 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 optionsSaveFormDataMenuItem: 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 defaultFormData = false // Form data can be removed once the minimum API >= 26. private var defaultProgressViewEndOffset = 0 private var defaultProgressViewStartOffset = 0 private var defaultJavaScript = false @@ -420,23 +418,17 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook 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) { - // Initialize the file name string from the file URI last path segment. - var fileNameString = fileUri.lastPathSegment + // Get a cursor from the content resolver. + val contentResolverCursor = contentResolver.query(fileUri, null, null, null)!! - // Query the exact file name if the API >= 26. - if (Build.VERSION.SDK_INT >= 26) { - // Get a cursor from the content resolver. - val contentResolverCursor = contentResolver.query(fileUri, null, null, null)!! + // Move to the fist row. + contentResolverCursor.moveToFirst() - // Move to the fist row. - contentResolverCursor.moveToFirst() + // Get the file name from the cursor. + val fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) - // Get the file name from the cursor. - fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) - - // Close the cursor. - contentResolverCursor.close() - } + // Close the cursor. + contentResolverCursor.close() // Use a coroutine to save the file. CoroutineScope(Dispatchers.Main).launch { @@ -532,7 +524,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook 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. @@ -646,7 +638,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Initialize the WebView state adapter. webViewStateAdapter = WebViewStateAdapter(this, bottomAppBar) - // Set the pager adapter on the web view pager. + // Set the WebView pager adapter. webViewViewPager2.adapter = webViewStateAdapter // Store up to 100 tabs in memory. @@ -666,6 +658,15 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Update the bookmarks drawer pinned image view. updateBookmarksDrawerPinnedImageView() + // Get the default favorite icon drawable. + val favoriteIconDrawable = AppCompatResources.getDrawable(this, R.drawable.world) + + // Cast the favorite icon drawable to a bitmap drawable. + val favoriteIconBitmapDrawable = (favoriteIconDrawable as BitmapDrawable?)!! + + // Store the default favorite icon bitmap. + defaultFavoriteIconBitmap = favoriteIconBitmapDrawable.bitmap + // Initialize the app. initializeApp() @@ -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. - // 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(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) @@ -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) - optionsSaveFormDataMenuItem = menu.findItem(R.id.save_form_data) // Form data can be removed once the minimum API >= 26. optionsClearDataMenuItem = menu.findItem(R.id.clear_data) optionsClearCookiesMenuItem = menu.findItem(R.id.clear_cookies) optionsClearDomStorageMenuItem = menu.findItem(R.id.clear_dom_storage) - optionsClearFormDataMenuItem = menu.findItem(R.id.clear_form_data) // Form data can be removed once the minimum API >= 26. optionsEasyListMenuItem = menu.findItem(R.id.easylist) optionsEasyPrivacyMenuItem = menu.findItem(R.id.easyprivacy) optionsFanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list) @@ -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) - // 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) @@ -1113,7 +1079,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Set the title. optionsRefreshMenuItem.setTitle(R.string.stop) - // Set the icon if it is displayed in the app bar. Once the minimum API is >= 26, the blue and black icons can be combined with a tint list. + // Set the icon if it is displayed in the app bar. if (displayAdditionalAppBarIcons) optionsRefreshMenuItem.setIcon(R.drawable.close_blue) } @@ -1146,8 +1112,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Set the status of the menu item checkboxes. optionsDomStorageMenuItem.isChecked = currentWebView!!.settings.domStorageEnabled - @Suppress("DEPRECATION") - optionsSaveFormDataMenuItem.isChecked = currentWebView!!.settings.saveFormData // Form data can be removed once the minimum API >= 26. optionsEasyListMenuItem.isChecked = currentWebView!!.easyListEnabled optionsEasyPrivacyMenuItem.isChecked = currentWebView!!.easyPrivacyEnabled optionsFanboysAnnoyanceListMenuItem.isChecked = currentWebView!!.fanboysAnnoyanceListEnabled @@ -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 Form Data is there is any. This can be removed once the minimum API >= 26. - if (Build.VERSION.SDK_INT < 26) { - // Get the WebView database. - val webViewDatabase = WebViewDatabase.getInstance(this) - - // Enable the clear form data menu item if there is anything to clear. - @Suppress("DEPRECATION") - optionsClearFormDataMenuItem.isEnabled = webViewDatabase.hasFormData() - } - // Enable Clear Data if any of the submenu items are enabled. - optionsClearDataMenuItem.isEnabled = (optionsClearCookiesMenuItem.isEnabled || optionsClearDomStorageMenuItem.isEnabled || optionsClearFormDataMenuItem.isEnabled) + optionsClearDataMenuItem.isEnabled = (optionsClearCookiesMenuItem.isEnabled || optionsClearDomStorageMenuItem.isEnabled) // Disable Fanboy's Social Blocking List menu item if Fanboy's Annoyance List is checked. optionsFanboysSocialBlockingListMenuItem.isEnabled = !optionsFanboysAnnoyanceListMenuItem.isChecked @@ -1472,32 +1426,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook true } - R.id.save_form_data -> { // Form data. This can be removed once the minimum API >= 26. - // Switch the status of saveFormDataEnabled. - @Suppress("DEPRECATION") - currentWebView!!.settings.saveFormData = !currentWebView!!.settings.saveFormData - - // Update the menu checkbox. - @Suppress("DEPRECATION") - menuItem.isChecked = currentWebView!!.settings.saveFormData - - // Display a snackbar. - @Suppress("DEPRECATION") - if (currentWebView!!.settings.saveFormData) - Snackbar.make(webViewViewPager2, R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show() - else - Snackbar.make(webViewViewPager2, R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show() - - // Update the privacy icon. - updatePrivacyIcons(true) - - // Reload the current WebView. - currentWebView!!.reload() - - // Consume the event. - true - } - R.id.clear_cookies -> { // Clear cookies. // Create a snackbar. Snackbar.make(webViewViewPager2, R.string.cookies_deleted, Snackbar.LENGTH_LONG) @@ -1573,28 +1501,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook true } - R.id.clear_form_data -> { // Clear form data. This can be remove once the minimum API >= 26. - // Create a snackbar. - Snackbar.make(webViewViewPager2, R.string.form_data_deleted, Snackbar.LENGTH_LONG) - .setAction(R.string.undo) {} // Everything will be handled by `onDismissed()` below. - .addCallback(object : Snackbar.Callback() { - override fun onDismissed(snackbar: Snackbar, event: Int) { - if (event != DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed. - // Get a handle for the webView database. - val webViewDatabase = WebViewDatabase.getInstance(applicationContext) - - // Delete the form data. - @Suppress("DEPRECATION") - webViewDatabase.clearFormData() - } - } - }) - .show() - - // Consume the event. - true - } - R.id.easylist -> { // EasyList. // Toggle the EasyList status. currentWebView!!.easyListEnabled = !currentWebView!!.easyListEnabled @@ -1948,9 +1854,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Set the focus on the find on page edit text. findOnPageEditText.requestFocus() - // Get a handle for the input method manager. - val inputMethodManager = (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager) - // Display the keyboard. `0` sets no input flags. inputMethodManager.showSoftInput(findOnPageEditText, 0) }, 200) @@ -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. - 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) @@ -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) - domainsIntent.putExtra(CURRENT_URL, currentWebView!!.url) 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)) - // 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 @@ -2221,7 +2110,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Set the font size integer. val fontSizeInt = if (textZoomInt == defaultFontSizeString.toInt()) // The current system default is used, which is encoded as a zoom of `0`. - 0 + SYSTEM_DEFAULT else // A custom font size is used. textZoomInt @@ -2261,7 +2150,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook 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) @@ -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) - domainsIntent.putExtra(CURRENT_URL, currentWebView!!.url) 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()) { - // 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(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()) { - // 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(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. - domainsIntent.putExtra(CURRENT_URL, currentWebView!!.url) 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. - 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) @@ -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. - 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) } @@ -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. - 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) @@ -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. - 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) @@ -3007,17 +2844,21 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Clear the focus from the URL edit text, so that it will be populated with the information from the new tab. urlEditText.clearFocus() - // Get the new tab position. - val newTabPosition = if (adjacent) // The new tab position is immediately to the right of the current tab position. - tabLayout.selectedTabPosition + 1 - else // The new tab position is at the end. The tab positions are 0 indexed, so the new page number will match the current count. - tabLayout.tabCount + // Add the new tab after the tab layout has quiesced. + // Otherwise, there can be problems when restoring a large number of tabs and processing a new intent at the same time. + tabLayout.post { + // Get the new tab position. + val newTabPosition = if (adjacent) // The new tab position is immediately to the right of the current tab position. + tabLayout.selectedTabPosition + 1 + else // The new tab position is at the end. The tab positions are 0 indexed, so the new page number will match the current count. + tabLayout.tabCount - // Add the new WebView page. - webViewStateAdapter!!.addPage(newTabPosition, urlString) + // Add the new WebView page. + webViewStateAdapter!!.addPage(newTabPosition, urlString) - // Add the new tab. - addNewTab(newTabPosition, moveToTab) + // Add the new tab. + addNewTab(newTabPosition, moveToTab) + } } private fun addNewTab(newTabPosition: Int, moveToTab: Boolean) { @@ -3070,7 +2911,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook defaultJavaScript = sharedPreferences.getBoolean(getString(R.string.javascript_key), false) defaultCookies = sharedPreferences.getBoolean(getString(R.string.cookies_key), false) defaultDomStorage = sharedPreferences.getBoolean(getString(R.string.dom_storage_key), false) - defaultFormData = sharedPreferences.getBoolean(getString(R.string.save_form_data_key), false) // Form data can be removed once the minimum API >= 26. defaultEasyList = sharedPreferences.getBoolean(getString(R.string.easylist_key), true) defaultEasyPrivacy = sharedPreferences.getBoolean(getString(R.string.easyprivacy_key), true) defaultFanboysAnnoyanceList = sharedPreferences.getBoolean(getString(R.string.fanboys_annoyance_list_key), true) @@ -3085,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) - // Get the WebView theme entry values string array. This is done here so that expensive resource requests are not made each time a domain is loaded. + // Get the string arrays. These are done here so that expensive resource requests are not made each time a domain is loaded. webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values) - - // Get the user agent 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) + 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) @@ -3103,8 +2942,11 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook proxyMode = sharedPreferences.getString(getString(R.string.proxy_key), getString(R.string.proxy_default_value))!! fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean(getString(R.string.full_screen_browsing_mode_key), false) hideAppBar = sharedPreferences.getBoolean(getString(R.string.hide_app_bar_key), true) - downloadWithExternalApp = sharedPreferences.getBoolean(getString(R.string.download_with_external_app_key), false) - scrollAppBar = sharedPreferences.getBoolean(getString(R.string.scroll_app_bar_key), true) + val downloadProvider = sharedPreferences.getString(getString(R.string.download_provider_key), getString(R.string.download_provider_default_value))!! + scrollAppBar = sharedPreferences.getBoolean(getString(R.string.scroll_app_bar_key), false) + + // Determine if downloading should be handled by an external app. + downloadWithExternalApp = (downloadProvider == downloadProviderEntryValuesStringArray[2]) // Apply the saved proxy mode if the app has been restarted. if (savedProxyMode != null) { @@ -3261,10 +3103,10 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook nestedScrollWebView.clearPinnedSslCertificate() nestedScrollWebView.pinnedIpAddresses = "" - // Reset the favorite icon if specified. + // Reset the tab if specified. if (resetTab) { // Initialize the favorite icon. - nestedScrollWebView.initializeFavoriteIcon() + nestedScrollWebView.resetFavoriteIcon() // Get the current page position. val currentPagePosition = webViewStateAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId) @@ -3286,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. - 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) @@ -3335,7 +3177,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook val javaScriptInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ENABLE_JAVASCRIPT)) val cookiesInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(COOKIES)) val domStorageInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ENABLE_DOM_STORAGE)) - val formDataInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ENABLE_FORM_DATA)) // Form data can be removed once the minimum API >= 26. val easyListInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ENABLE_EASYLIST)) val easyPrivacyInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ENABLE_EASYPRIVACY)) val fanboysAnnoyanceListInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ENABLE_FANBOYS_ANNOYANCE_LIST)) @@ -3388,17 +3229,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook DISABLED -> nestedScrollWebView.settings.domStorageEnabled = false } - // Apply the form data setting if the API < 26. - @Suppress("DEPRECATION") - if (Build.VERSION.SDK_INT < 26) { - // Set the form data status. - when (formDataInt) { - SYSTEM_DEFAULT -> nestedScrollWebView.settings.saveFormData = defaultFormData - ENABLED -> nestedScrollWebView.settings.saveFormData = true - DISABLED -> nestedScrollWebView.settings.saveFormData = false - } - } - // Set the EasyList status. when (easyListInt) { SYSTEM_DEFAULT -> nestedScrollWebView.easyListEnabled = defaultEasyList @@ -3603,11 +3433,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Apply the default cookie setting. cookieManager.setAcceptCookie(nestedScrollWebView.acceptCookies) - // Apply the form data setting if the API < 26. - if (Build.VERSION.SDK_INT < 26) - @Suppress("DEPRECATION") - nestedScrollWebView.settings.saveFormData = defaultFormData - // Apply the default font size setting. try { // Try to set the font size from the value in the app settings. @@ -3918,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 { @@ -4052,9 +3857,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Show the toolbar. toolbar.visibility = View.VISIBLE - // Get a handle for the input method manager. - val inputMethodManager = (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager) - // Hide the keyboard. inputMethodManager.hideSoftInputFromWindow(toolbar.windowToken, 0) } @@ -4078,17 +3880,32 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook } } - override fun createBookmark(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap) { + override fun createBookmark(dialogFragment: DialogFragment) { // Get the dialog. val dialog = dialogFragment.dialog!! // Get the views from the dialog fragment. - val createBookmarkNameEditText = dialog.findViewById(R.id.create_bookmark_name_edittext) - val createBookmarkUrlEditText = dialog.findViewById(R.id.create_bookmark_url_edittext) + val webpageFavoriteIconRadioButton = dialog.findViewById(R.id.webpage_favorite_icon_radiobutton) + val webpageFavoriteIconImageView = dialog.findViewById(R.id.webpage_favorite_icon_imageview) + val customIconImageView = dialog.findViewById(R.id.custom_icon_imageview) + val bookmarkNameEditText = dialog.findViewById(R.id.bookmark_name_edittext) + val bookmarkUrlEditText = dialog.findViewById(R.id.bookmark_url_edittext) // Extract the strings from the edit texts. - val bookmarkNameString = createBookmarkNameEditText.text.toString() - val bookmarkUrlString = createBookmarkUrlEditText.text.toString() + val bookmarkNameString = bookmarkNameEditText.text.toString() + val bookmarkUrlString = bookmarkUrlEditText.text.toString() + + // Get the selected favorite icon drawable. + val favoriteIconDrawable = if (webpageFavoriteIconRadioButton.isChecked) // Use the webpage favorite icon. + webpageFavoriteIconImageView.drawable + else // Use the custom icon. + customIconImageView.drawable + + // Cast the favorite icon bitmap to a bitmap drawable + val favoriteIconBitmapDrawable = favoriteIconDrawable as BitmapDrawable + + // Convert the favorite icon bitmap drawable to a bitmap. + val favoriteIconBitmap = favoriteIconBitmapDrawable.bitmap // Create a favorite icon byte array output stream. val favoriteIconByteArrayOutputStream = ByteArrayOutputStream() @@ -4115,32 +3932,34 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook bookmarksListView.setSelection(newBookmarkDisplayOrder) } - override fun createBookmarkFolder(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap) { + override fun createBookmarkFolder(dialogFragment: DialogFragment) { // Get the dialog. val dialog = dialogFragment.dialog!! // Get handles for the views in the dialog fragment. + val defaultFolderIconRadioButton = dialog.findViewById(R.id.default_folder_icon_radiobutton) + val defaultFolderIconImageView = dialog.findViewById(R.id.default_folder_icon_imageview) + val webpageFavoriteIconRadioButton = dialog.findViewById(R.id.webpage_favorite_icon_radiobutton) + val webpageFavoriteIconImageView = dialog.findViewById(R.id.webpage_favorite_icon_imageview) + val customIconImageView = dialog.findViewById(R.id.custom_icon_imageview) val folderNameEditText = dialog.findViewById(R.id.folder_name_edittext) - val defaultIconRadioButton = dialog.findViewById(R.id.default_icon_radiobutton) - val defaultIconImageView = dialog.findViewById(R.id.default_icon_imageview) // Get new folder name string. val folderNameString = folderNameEditText.text.toString() // Set the folder icon bitmap according to the dialog. - val folderIconBitmap: Bitmap = if (defaultIconRadioButton.isChecked) { // Use the default folder icon. - // Get the default folder icon drawable. - val folderIconDrawable = defaultIconImageView.drawable - - // Convert the folder icon drawable to a bitmap drawable. - val folderIconBitmapDrawable = folderIconDrawable as BitmapDrawable - - // Convert the folder icon bitmap drawable to a bitmap. - folderIconBitmapDrawable.bitmap - } else { // Use the WebView favorite icon. - // Copy the favorite icon bitmap to the folder icon bitmap. - favoriteIconBitmap - } + val folderIconDrawable = if (defaultFolderIconRadioButton.isChecked) // Use the default folder icon. + defaultFolderIconImageView.drawable + else if (webpageFavoriteIconRadioButton.isChecked) // Use the webpage favorite icon. + webpageFavoriteIconImageView.drawable + else // Use the custom icon. + customIconImageView.drawable + + // Cast the folder icon bitmap to a bitmap drawable. + val folderIconBitmapDrawable = folderIconDrawable as BitmapDrawable + + // Convert the folder icon bitmap drawable to a bitmap. + val folderIconBitmap = folderIconBitmapDrawable.bitmap // Create a folder icon byte array output stream. val folderIconByteArrayOutputStream = ByteArrayOutputStream() @@ -4173,20 +3992,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook bookmarksListView.setSelection(0) } - private fun downloadUrlWithExternalApp(url: String) { - // Create a download intent. Not specifying the action type will display the maximum number of options. - val downloadIntent = Intent() - - // Set the URI and the mime type. - downloadIntent.setDataAndType(Uri.parse(url), "text/html") - - // Flag the intent to open in a new task. - downloadIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK - - // Show the chooser. - startActivity(Intent.createChooser(downloadIntent, getString(R.string.download_with_external_app))) - } - private fun exitFullScreenVideo() { // Re-enable the screen timeout. fullScreenVideoFrameLayout.keepScreenOn = false @@ -4345,7 +4150,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook @SuppressLint("ClickableViewAccessibility") private fun initializeApp() { // Get a handle for the input method. - val inputMethodManager = (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager) + inputMethodManager = (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager) // Initialize the color spans for highlighting the URLs. initialGrayColorSpan = ForegroundColorSpan(getColor(R.color.gray_500)) @@ -4534,11 +4339,15 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Search for the string on the page whenever a character changes in the find on page edit text. findOnPageEditText.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} + override fun beforeTextChanged(charSequence: CharSequence, start: Int, count: Int, after: Int) { + // Do nothing. + } - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {} + override fun onTextChanged(charSequence: CharSequence, start: Int, before: Int, count: Int) { + // Do nothing. + } - override fun afterTextChanged(s: Editable) { + override fun afterTextChanged(editable: Editable) { // Search for the text in the WebView if it is not null. Sometimes on resume after a period of non-use the WebView will be null. currentWebView?.findAllAsync(findOnPageEditText.text.toString()) } @@ -4793,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 @@ -4969,13 +4775,12 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook registerForContextMenu(nestedScrollWebView) // Allow the downloading of files. - nestedScrollWebView.setDownloadListener { downloadUrlString: String?, userAgent: String?, contentDisposition: String?, mimetype: String?, contentLength: Long -> - // Check the download preference. + nestedScrollWebView.setDownloadListener { downloadUrlString: String, userAgent: String, contentDisposition: String, mimetype: String, contentLength: Long -> + // Use the specified download provider. if (downloadWithExternalApp) { // Download with an external app. - downloadUrlWithExternalApp(downloadUrlString!!) - } else { // Handle the download inside of Privacy Browser. - // Define a formatted file size string. - + // Download with an external app. + saveWithExternalApp(downloadUrlString) + } else { // Download with Privacy Browser or Android's download manager. // Process the content length if it contains data. val formattedFileSizeString = if (contentLength > 0) { // The content length is greater than 0. // Format the content length as a string. @@ -4986,10 +4791,10 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook } // Get the file name from the content disposition. - val fileNameString = UrlHelper.getFileName(this, contentDisposition, mimetype, downloadUrlString!!) + val fileNameString = UrlHelper.getFileName(this, contentDisposition, mimetype, downloadUrlString) - // Instantiate the save dialog. - val saveDialogFragment = SaveDialog.saveUrl(downloadUrlString, fileNameString, formattedFileSizeString, userAgent!!, nestedScrollWebView.acceptCookies) + // Instantiate the save dialog according. + val saveDialogFragment = SaveDialog.saveUrl(downloadUrlString, fileNameString, formattedFileSizeString, userAgent, nestedScrollWebView.acceptCookies) // Try to show the dialog. The download listener continues to function even when the WebView is paused. Attempting to display a dialog in that state leads to a crash. try { @@ -5115,7 +4920,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook val tabFavoriteIconImageView = tabView.findViewById(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)) } } } @@ -5646,8 +5451,14 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Instantiate an HTTP authentication dialog. val httpAuthenticationDialogFragment = HttpAuthenticationDialog.displayDialog(host, realm, nestedScrollWebView.webViewFragmentId) - // Show the HTTP authentication dialog. - httpAuthenticationDialogFragment.show(supportFragmentManager, getString(R.string.http_authentication)) + // Try to show the dialog. WebView can receive an HTTP authentication request even after the app has been paused. Attempting to display a dialog in that state leads to a crash. + try { + // Show the HTTP authentication dialog. + httpAuthenticationDialogFragment.show(supportFragmentManager, getString(R.string.http_authentication)) + } catch (exception: Exception) { // The dialog could not be shown. + // Add the dialog to the pending dialog array list. It will be displayed in `onStart()`. + pendingDialogsArrayList.add(PendingDialogDataClass(httpAuthenticationDialogFragment, getString(R.string.http_authentication))) + } } override fun onPageStarted(webView: WebView, url: String, favicon: Bitmap?) { @@ -5885,7 +5696,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Instantiate an SSL certificate error alert dialog. val sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error, nestedScrollWebView.webViewFragmentId) - // Try to show the dialog. The SSL error handler continues to function even when the WebView is paused. Attempting to display a dialog in that state leads to a crash. + // Try to show the dialog. The SSL error handler continues to function even when the app has been stopped. Attempting to display a dialog in that state leads to a crash. try { // Show the SSL certificate error dialog. sslCertificateErrorDialogFragment.show(supportFragmentManager, getString(R.string.ssl_certificate_error)) @@ -6084,15 +5895,56 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook loadUrl(currentWebView!!, urlString) } - override fun navigateHistory(url: String, steps: Int) { + override fun navigateHistory(steps: Int) { + // Get the current web back forward list. + val webBackForwardList = currentWebView!!.copyBackForwardList() + + // Calculate the target index. + val targetIndex = webBackForwardList.currentIndex + steps + + // Get the previous entry data. + val previousUrl = webBackForwardList.getItemAtIndex(targetIndex).url + val previousFavoriteIcon = webBackForwardList.getItemAtIndex(targetIndex).favicon + // Apply the domain settings. - applyDomainSettings(currentWebView!!, url, resetTab = false, reloadWebsite = false, loadUrl = false) + applyDomainSettings(currentWebView!!, previousUrl, resetTab = false, reloadWebsite = false, loadUrl = false) + + // Get the current tab. + val tab = tabLayout.getTabAt(tabLayout.selectedTabPosition)!! + + // Get the custom view from the tab. + val tabView = tab.customView!! + + // Get the favorite icon image view from the tab. + val tabFavoriteIconImageView = tabView.findViewById(R.id.favorite_icon_imageview) + + // Store the previous favorite icon. + if (previousFavoriteIcon == null) + currentWebView!!.setFavoriteIcon(defaultFavoriteIconBitmap) + else + currentWebView!!.setFavoriteIcon(previousFavoriteIcon) + + // Display the previous favorite icon in the tab. + tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(currentWebView!!.getFavoriteIcon(), 128, 128, true)) // Load the history entry. currentWebView!!.goBackOrForward(steps) - // Update the URL edit text after a delay. - updateUrlEditTextAfterDelay() + // Create a handler to update the URL edit box. + val urlEditTextUpdateHandler = Handler(Looper.getMainLooper()) + + // Create a runnable to update the URL edit box. + val urlEditTextUpdateRunnable = Runnable { + // Update the URL edit text. + urlEditText.setText(currentWebView!!.url) + + // Disable the wide viewport if the source is being viewed. + if (currentWebView!!.url!!.startsWith("view-source:")) + currentWebView!!.settings.useWideViewPort = false + } + + // Update the URL edit text after 50 milliseconds, so that the WebView has enough time to navigate to the new URL. + urlEditTextUpdateHandler.postDelayed(urlEditTextUpdateRunnable, 50) } override fun openFile(dialogFragment: DialogFragment) { @@ -6197,20 +6049,8 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook } override fun pinnedErrorGoBack() { - // Get the current web back forward list. - val webBackForwardList = currentWebView!!.copyBackForwardList() - - // Get the previous entry URL. - val previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.currentIndex - 1).url - - // Apply the domain settings. - applyDomainSettings(currentWebView!!, previousUrl, resetTab = false, reloadWebsite = false, loadUrl = false) - - // Go back. - currentWebView!!.goBack() - - // Update the URL edit text after a delay. - updateUrlEditTextAfterDelay() + // Navigate back one page. + navigateHistory(-1) } private fun sanitizeUrl(urlString: String): String { @@ -6229,7 +6069,76 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook return sanitizedUrlString } - override fun saveUrl(originalUrlString: String, fileNameString: String, dialogFragment: DialogFragment) { + override fun saveWithAndroidDownloadManager(dialogFragment: DialogFragment) { + // Get the dialog. + val dialog = dialogFragment.dialog!! + + // Get handles for the dialog views. + val dialogUrlEditText = dialog.findViewById(R.id.url_edittext) + val downloadDirectoryRadioGroup = dialog.findViewById(R.id.download_directory_radiogroup) + val dialogFileNameEditText = dialog.findViewById(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 . + 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. @@ -6241,7 +6150,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Get a handle for the dialog URL edit text. val dialogUrlEditText = dialog.findViewById(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() } @@ -6279,9 +6188,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Update the privacy icons. `true` redraws the icons in the app bar. updatePrivacyIcons(true) - // Get a handle for the input method manager. - val inputMethodManager = (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager) - // Get the current URL. val urlString = currentWebView!!.url @@ -6429,22 +6335,4 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook invalidateOptionsMenu() } } - - fun updateUrlEditTextAfterDelay() { - // Create a handler to update the URL edit box. - val urlEditTextUpdateHandler = Handler(Looper.getMainLooper()) - - // Create a runnable to update the URL edit box. - val urlEditTextUpdateRunnable = Runnable { - // Update the URL edit text. - urlEditText.setText(currentWebView!!.url) - - // Disable the wide viewport if the source is being viewed. - if (currentWebView!!.url!!.startsWith("view-source:")) - currentWebView!!.settings.useWideViewPort = false - } - - // Update the URL edit text after 50 milliseconds, so that the WebView has enough time to navigate to the new URL. - urlEditTextUpdateHandler.postDelayed(urlEditTextUpdateRunnable, 50) - } }