import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.DialogFragment
import androidx.preference.PreferenceManager
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
-import androidx.viewpager.widget.ViewPager
+import androidx.viewpager2.widget.ViewPager2
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature
import com.google.android.material.tabs.TabLayout
import com.stoutner.privacybrowser.R
-import com.stoutner.privacybrowser.adapters.WebViewPagerAdapter
+import com.stoutner.privacybrowser.adapters.WebViewStateAdapter
import com.stoutner.privacybrowser.coroutines.GetHostIpAddressesCoroutine
import com.stoutner.privacybrowser.coroutines.PopulateFilterListsCoroutine
import com.stoutner.privacybrowser.coroutines.PrepareSaveDialogCoroutine
import com.stoutner.privacybrowser.dialogs.ViewSslCertificateDialog
import com.stoutner.privacybrowser.dialogs.WaitingForProxyDialog
import com.stoutner.privacybrowser.fragments.WebViewTabFragment
+import com.stoutner.privacybrowser.helpers.BOOKMARK_NAME
+import com.stoutner.privacybrowser.helpers.BOOKMARK_URL
+import com.stoutner.privacybrowser.helpers.COOKIES
+import com.stoutner.privacybrowser.helpers.DARK_THEME
+import com.stoutner.privacybrowser.helpers.DISABLED
+import com.stoutner.privacybrowser.helpers.DISPLAY_IMAGES
+import com.stoutner.privacybrowser.helpers.DOMAIN_NAME
+import com.stoutner.privacybrowser.helpers.ENABLE_DOM_STORAGE
+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
+import com.stoutner.privacybrowser.helpers.FAVORITE_ICON
+import com.stoutner.privacybrowser.helpers.FOLDER_ID
+import com.stoutner.privacybrowser.helpers.FONT_SIZE
+import com.stoutner.privacybrowser.helpers.ID
+import com.stoutner.privacybrowser.helpers.IP_ADDRESSES
+import com.stoutner.privacybrowser.helpers.IS_FOLDER
+import com.stoutner.privacybrowser.helpers.LIGHT_THEME
+import com.stoutner.privacybrowser.helpers.PINNED_IP_ADDRESSES
+import com.stoutner.privacybrowser.helpers.PINNED_SSL_CERTIFICATE
import com.stoutner.privacybrowser.helpers.REQUEST_ALLOWED
import com.stoutner.privacybrowser.helpers.REQUEST_BLOCKED
import com.stoutner.privacybrowser.helpers.REQUEST_DEFAULT
import com.stoutner.privacybrowser.helpers.REQUEST_THIRD_PARTY
+import com.stoutner.privacybrowser.helpers.SSL_ISSUED_BY_COMMON_NAME
+import com.stoutner.privacybrowser.helpers.SSL_ISSUED_BY_ORGANIZATION
+import com.stoutner.privacybrowser.helpers.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT
+import com.stoutner.privacybrowser.helpers.SSL_ISSUED_TO_COMMON_NAME
+import com.stoutner.privacybrowser.helpers.SSL_ISSUED_TO_ORGANIZATION
+import com.stoutner.privacybrowser.helpers.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT
+import com.stoutner.privacybrowser.helpers.SWIPE_TO_REFRESH
+import com.stoutner.privacybrowser.helpers.SYSTEM_DEFAULT
+import com.stoutner.privacybrowser.helpers.WEBVIEW_THEME
+import com.stoutner.privacybrowser.helpers.WIDE_VIEWPORT
import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper
import com.stoutner.privacybrowser.helpers.CheckFilterListHelper
import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper
import com.stoutner.privacybrowser.views.FANBOYS_ANNOYANCE_LIST
import com.stoutner.privacybrowser.views.FANBOYS_SOCIAL_BLOCKING_LIST
import com.stoutner.privacybrowser.views.THIRD_PARTY_REQUESTS
-import com.stoutner.privacybrowser.views.ULTRALIST
import com.stoutner.privacybrowser.views.ULTRAPRIVACY
import com.stoutner.privacybrowser.views.NestedScrollWebView
companion object {
// Define the public static variables.
- var currentBookmarksFolder = ""
+ var currentBookmarksFolderId = 0L
val executorService = Executors.newFixedThreadPool(4)!!
var orbotStatus = "unknown"
val pendingDialogsArrayList = ArrayList<PendingDialogDataClass>()
var proxyMode = ProxyHelper.NONE
var restartFromBookmarksActivity = false
- var webViewPagerAdapter: WebViewPagerAdapter? = null
+ var webViewStateAdapter: WebViewStateAdapter? = null
// Declare the public static variables.
lateinit var appBarLayout: AppBarLayout
private lateinit var bookmarksTitleTextView: TextView
private lateinit var coordinatorLayout: CoordinatorLayout
private lateinit var cookieManager: CookieManager
+ private lateinit var defaultFontSizeString: String
+ private lateinit var defaultUserAgentName: String
+ private lateinit var defaultWebViewTheme: String
private lateinit var domainsSettingsSet: MutableSet<String>
private lateinit var drawerLayout: DrawerLayout
private lateinit var easyList: ArrayList<List<Array<String>>>
private lateinit var navigationForwardMenuItem: MenuItem
private lateinit var navigationHistoryMenuItem: MenuItem
private lateinit var navigationRequestsMenuItem: MenuItem
+ private lateinit var navigationView: NavigationView
private lateinit var optionsAddOrEditDomainMenuItem: MenuItem
private lateinit var optionsBlockAllThirdPartyRequestsMenuItem: MenuItem
private lateinit var optionsClearCookiesMenuItem: MenuItem
private lateinit var optionsUserAgentSafariOnIosMenuItem: MenuItem
private lateinit var optionsUserAgentSafariOnMacosMenuItem: MenuItem
private lateinit var optionsUserAgentWebViewDefaultMenuItem: MenuItem
+ private lateinit var optionsViewSourceMenuItem: MenuItem
private lateinit var optionsWideViewportMenuItem: MenuItem
private lateinit var proxyHelper: ProxyHelper
private lateinit var redColorSpan: ForegroundColorSpan
private lateinit var tabsLinearLayout: LinearLayout
private lateinit var toolbar: Toolbar
private lateinit var webViewDefaultUserAgent: String
- private lateinit var webViewPager: ViewPager
+ private lateinit var webViewThemeEntryValuesStringArray: Array<String>
+ private lateinit var webViewViewPager2: ViewPager2
private lateinit var ultraList: ArrayList<List<Array<String>>>
private lateinit var urlEditText: EditText
private lateinit var urlRelativeLayout: RelativeLayout
+ private lateinit var userAgentDataArray: Array<String>
+ private lateinit var userAgentNamesArray: ArrayAdapter<CharSequence>
// Define the class variables.
private var actionBarDrawerToggle: ActionBarDrawerToggle? = null
private var bookmarksDrawerPinned = false
private var bottomAppBar = false
private var currentWebView: NestedScrollWebView? = null
+ private var defaultBlockAllThirdPartyRequests = false
+ private var defaultCookies = false
+ private var defaultDisplayWebpageImages = true
+ private var defaultDomStorage = false
+ private var defaultEasyList = 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 defaultSwipeToRefresh = true
+ private var defaultUltraList = true
+ private var defaultUltraPrivacy = true
+ private var defaultWideViewport = true
private var displayAdditionalAppBarIcons = false
private var displayingFullScreenVideo = false
private var domainsDatabaseHelper: DomainsDatabaseHelper? = null
private var inFullScreenBrowsingMode = false
private var incognitoModeEnabled = false
private var loadingNewIntent = false
+ private var navigationDrawerFirstView = true
private var objectAnimator = ObjectAnimator()
private var optionsMenu: Menu? = null
private var orbotStatusBroadcastReceiver: BroadcastReceiver? = null
private var reapplyAppSettingsOnRestart = false
private var reapplyDomainSettingsOnRestart = false
+ private var restartTime = Date(0)
private var sanitizeAmpRedirects = false
private var sanitizeTrackingQueries = false
private var savedProxyMode: String? = null
tabsLinearLayout = findViewById(R.id.tabs_linearlayout)
tabLayout = findViewById(R.id.tablayout)
swipeRefreshLayout = findViewById(R.id.swiperefreshlayout)
- webViewPager = findViewById(R.id.webviewpager)
- val navigationView = findViewById<NavigationView>(R.id.navigationview)
+ webViewViewPager2 = findViewById(R.id.webview_viewpager2)
+ navigationView = findViewById(R.id.navigationview)
bookmarksListView = findViewById(R.id.bookmarks_drawer_listview)
bookmarksTitleTextView = findViewById(R.id.bookmarks_title_textview)
bookmarksDrawerPinnedImageView = findViewById(R.id.bookmarks_drawer_pinned_imageview)
// Initially hide the user interface so that only the filter list loading screen is shown (if reloading).
drawerLayout.visibility = View.GONE
- // Initialize the WebView pager adapter.
- webViewPagerAdapter = WebViewPagerAdapter(supportFragmentManager)
+ // Initialize the WebView state adapter.
+ webViewStateAdapter = WebViewStateAdapter(this)
// Set the pager adapter on the web view pager.
- webViewPager.adapter = webViewPagerAdapter
+ webViewViewPager2.adapter = webViewStateAdapter
// Store up to 100 tabs in memory.
- webViewPager.offscreenPageLimit = 100
+ webViewViewPager2.offscreenPageLimit = 100
+
+ // Disable swiping between pages in the view pager.
+ webViewViewPager2.isUserInputEnabled = false
// Get a handle for the cookie manager.
cookieManager = CookieManager.getInstance()
// Go back.
currentWebView!!.goBack()
+
+ // Update the URL edit text after a delay.
+ updateUrlEditTextAfterDelay()
} else { // Close the current tab.
// A view is required because the method is also called by an XML `onClick`.
closeTab(null)
}
} else { // The app has been restarted.
// If the new intent will open a new tab, set the saved tab position to be the size of the saved state array list.
- // The tab position is 0 based, meaning the at the new tab will be the tab position that is restored.
+ // The tab position is 0 based, meaning the new tab will be the tab position that is restored.
if ((intentUriData != null) || (intentStringExtra != null) || isWebSearch)
savedTabPosition = savedStateArrayList!!.size
updateDomainsSettingsSet()
// Reapply the domain settings for each tab.
- for (i in 0 until webViewPagerAdapter!!.count) {
+ for (i in 0 until webViewStateAdapter!!.itemCount) {
// Get the WebView tab fragment.
- val webViewTabFragment = webViewPagerAdapter!!.getPageFragment(i)
+ val webViewTabFragment = webViewStateAdapter!!.getPageFragment(i)
// Get the fragment view.
val fragmentView = webViewTabFragment.view
// Run the default commands.
super.onStart()
- // Resume any WebViews if the pager adapter exists. If the app is restarting to change the initial app theme it won't have been populated yet.
- if (webViewPagerAdapter != null) {
- for (i in 0 until webViewPagerAdapter!!.count) {
+ // Resume any WebViews if the state adapter exists. If the app is restarting to change the initial app theme it won't have been populated yet.
+ if (webViewStateAdapter != null) {
+ for (i in 0 until webViewStateAdapter!!.itemCount) {
// Get the WebView tab fragment.
- val webViewTabFragment = webViewPagerAdapter!!.getPageFragment(i)
+ val webViewTabFragment = webViewStateAdapter!!.getPageFragment(i)
// Get the fragment view.
val fragmentView = webViewTabFragment.view
// Run the default commands.
super.onSaveInstanceState(savedInstanceState)
- // Only save the instance state if the WebView pager adapter is not null, which will be the case if the app is restarting to change the initial app theme.
- if (webViewPagerAdapter != 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) {
// Initialize the saved state array lists.
savedStateArrayList = ArrayList<Bundle>()
savedNestedScrollWebViewStateArrayList = ArrayList<Bundle>()
// Get the URLs from each tab.
- for (i in 0 until webViewPagerAdapter!!.count) {
+ for (i in 0 until webViewStateAdapter!!.itemCount) {
// Get the WebView tab fragment.
- val webViewTabFragment = webViewPagerAdapter!!.getPageFragment(i)
+ val webViewTabFragment = webViewStateAdapter!!.getPageFragment(i)
// Get the fragment view.
val fragmentView = webViewTabFragment.view
// Run the default commands.
super.onStop()
- // Only pause the WebViews if the pager adapter is not null, which is the case if the app is restarting to change the initial app theme.
- if (webViewPagerAdapter != null) {
+ // Only pause the WebViews if the state adapter is not null, which is the case if the app is restarting to change the initial app theme.
+ if (webViewStateAdapter != null) {
// Pause each web view.
- for (i in 0 until webViewPagerAdapter!!.count) {
+ for (i in 0 until webViewStateAdapter!!.itemCount) {
// Get the WebView tab fragment.
- val webViewTabFragment = webViewPagerAdapter!!.getPageFragment(i)
+ val webViewTabFragment = webViewStateAdapter!!.getPageFragment(i)
// Get the fragment view.
val fragmentView = webViewTabFragment.view
super.onDestroy()
}
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ // Run the default commands.
+ super.onConfigurationChanged(newConfig)
+
+ // Reset the navigation drawer first view flag.
+ navigationDrawerFirstView = true
+
+ // Get the current page.
+ val currentPage = webViewViewPager2.currentItem
+
+ // Toggle the pages if there is more than one so that the view pager will recalculate their size.
+ if (currentPage > 0) {
+ // Switch to the previous page after 25 milliseconds.
+ webViewViewPager2.postDelayed ({ webViewViewPager2.currentItem = (currentPage - 1) }, 25)
+
+ // Switch back to the current page after the view pager has quiesced (which we are deciding should be 25 milliseconds).
+ webViewViewPager2.postDelayed ({ webViewViewPager2.currentItem = currentPage }, 25)
+ }
+
+ // Scroll to the current tab position after 25 milliseconds.
+ tabLayout.postDelayed ({ tabLayout.setScrollPosition(currentPage, 0F, false, false) }, 25)
+ }
+
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu. This adds items to the app bar if it is present.
menuInflater.inflate(R.menu.webview_options_menu, menu)
optionsDisplayImagesMenuItem = menu.findItem(R.id.display_images)
optionsDarkWebViewMenuItem = menu.findItem(R.id.dark_webview)
optionsFontSizeMenuItem = menu.findItem(R.id.font_size)
+ optionsViewSourceMenuItem = menu.findItem(R.id.view_source)
optionsAddOrEditDomainMenuItem = menu.findItem(R.id.add_or_edit_domain)
// Set the initial status of the privacy icons. `false` does not call `invalidateOptionsMenu` as the last step.
// 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
- // Only display the dark WebView menu item if the API >= 29.
- optionsDarkWebViewMenuItem.isVisible = Build.VERSION.SDK_INT >= 29
-
// 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 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.
+ 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
optionsWideViewportMenuItem.isChecked = currentWebView!!.settings.useWideViewPort
optionsDisplayImagesMenuItem.isChecked = currentWebView!!.settings.loadsImagesAutomatically
- // Initialize the display names for the filter lists with the number of blocked requests.
+ // Set the display names for the filter lists with the number of blocked requests.
optionsFilterListsMenuItem.title = getString(R.string.filterlists) + " - " + currentWebView!!.getRequestsCount(BLOCKED_REQUESTS)
optionsEasyListMenuItem.title = currentWebView!!.getRequestsCount(EASYLIST).toString() + " - " + getString(R.string.easylist)
optionsEasyPrivacyMenuItem.title = currentWebView!!.getRequestsCount(EASYPRIVACY).toString() + " - " + getString(R.string.easyprivacy)
optionsFanboysAnnoyanceListMenuItem.title = currentWebView!!.getRequestsCount(FANBOYS_ANNOYANCE_LIST).toString() + " - " + getString(R.string.fanboys_annoyance_list)
optionsFanboysSocialBlockingListMenuItem.title = currentWebView!!.getRequestsCount(FANBOYS_SOCIAL_BLOCKING_LIST).toString() + " - " + getString(R.string.fanboys_social_blocking_list)
- optionsUltraListMenuItem.title = currentWebView!!.getRequestsCount(ULTRALIST).toString() + " - " + getString(R.string.ultralist)
+ optionsUltraListMenuItem.title = currentWebView!!.getRequestsCount(com.stoutner.privacybrowser.views.ULTRALIST).toString() + " - " + getString(R.string.ultralist)
optionsUltraPrivacyMenuItem.title = currentWebView!!.getRequestsCount(ULTRAPRIVACY).toString() + " - " + getString(R.string.ultraprivacy)
optionsBlockAllThirdPartyRequestsMenuItem.title = currentWebView!!.getRequestsCount(THIRD_PARTY_REQUESTS).toString() + " - " + getString(R.string.block_all_third_party_requests)
// Enable dark WebView if night mode is enabled.
optionsDarkWebViewMenuItem.isEnabled = (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES)
- // Set the checkbox status for dark WebView if the device is running API >= 29 and algorithmic darkening is supported.
- if ((Build.VERSION.SDK_INT >= 29) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING))
+ // Set the checkbox status for dark WebView if algorithmic darkening is supported.
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING))
optionsDarkWebViewMenuItem.isChecked = WebSettingsCompat.isAlgorithmicDarkeningAllowed(currentWebView!!.settings)
+
+ // Set the view source title according to the current URL.
+ if (currentWebView!!.currentUrl.startsWith("view-source:"))
+ optionsViewSourceMenuItem.title = getString(R.string.view_rendered_website)
+ else
+ optionsViewSourceMenuItem.title = getString(R.string.view_source)
}
// Set the cookies menu item checked status.
// Display a snackbar.
if (currentWebView!!.settings.javaScriptEnabled) // JavaScrip is enabled.
- Snackbar.make(webViewPager, R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show()
+ Snackbar.make(webViewViewPager2, R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show()
else if (cookieManager.acceptCookie()) // JavaScript is disabled, but cookies are enabled.
- Snackbar.make(webViewPager, R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show()
+ Snackbar.make(webViewViewPager2, R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show()
else // Privacy mode.
- Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show()
+ Snackbar.make(webViewViewPager2, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show()
// Reload the current WebView.
currentWebView!!.reload()
// Display a snackbar.
if (cookieManager.acceptCookie()) // Cookies are enabled.
- Snackbar.make(webViewPager, R.string.cookies_enabled, Snackbar.LENGTH_SHORT).show()
+ Snackbar.make(webViewViewPager2, R.string.cookies_enabled, Snackbar.LENGTH_SHORT).show()
else if (currentWebView!!.settings.javaScriptEnabled) // JavaScript is still enabled.
- Snackbar.make(webViewPager, R.string.cookies_disabled, Snackbar.LENGTH_SHORT).show()
+ Snackbar.make(webViewViewPager2, R.string.cookies_disabled, Snackbar.LENGTH_SHORT).show()
else // Privacy mode.
- Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show()
+ Snackbar.make(webViewViewPager2, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show()
// Reload the current WebView.
currentWebView!!.reload()
// Display a snackbar.
if (currentWebView!!.settings.domStorageEnabled)
- Snackbar.make(webViewPager, R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show()
+ Snackbar.make(webViewViewPager2, R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show()
else
- Snackbar.make(webViewPager, R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show()
+ Snackbar.make(webViewViewPager2, R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show()
// Reload the current WebView.
currentWebView!!.reload()
// Display a snackbar.
@Suppress("DEPRECATION")
if (currentWebView!!.settings.saveFormData)
- Snackbar.make(webViewPager, R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show()
+ Snackbar.make(webViewViewPager2, R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show()
else
- Snackbar.make(webViewPager, R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show()
+ Snackbar.make(webViewViewPager2, R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show()
// Update the privacy icon.
updatePrivacyIcons(true)
R.id.clear_cookies -> { // Clear cookies.
// Create a snackbar.
- Snackbar.make(webViewPager, R.string.cookies_deleted, Snackbar.LENGTH_LONG)
+ Snackbar.make(webViewViewPager2, R.string.cookies_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) {
R.id.clear_dom_storage -> { // Clear DOM storage.
// Create a snackbar.
- Snackbar.make(webViewPager, R.string.dom_storage_deleted, Snackbar.LENGTH_LONG)
+ Snackbar.make(webViewViewPager2, R.string.dom_storage_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) {
R.id.clear_form_data -> { // Clear form data. This can be remove once the minimum API >= 26.
// Create a snackbar.
- Snackbar.make(webViewPager, R.string.form_data_deleted, Snackbar.LENGTH_LONG)
+ 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) {
R.id.dark_webview -> { // Dark WebView.
// Toggle dark WebView if supported.
- if ((Build.VERSION.SDK_INT >= 29) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING))
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING))
WebSettingsCompat.setAlgorithmicDarkeningAllowed(currentWebView!!.settings, !WebSettingsCompat.isAlgorithmicDarkeningAllowed(currentWebView!!.settings)
)
}
R.id.view_source -> { // View source.
- // Create an intent to launch the view source activity.
- val viewSourceIntent = Intent(this, ViewSourceActivity::class.java)
+ // Open a new tab according to the current URL.
+ if (currentWebView!!.currentUrl.startsWith("view-source:")) { // The source is currently viewed.
+ // Open the rendered website in a new tab.
+ addNewTab(currentWebView!!.currentUrl.substring(12, currentWebView!!.currentUrl.length), true)
+ } else { // The rendered website is currently viewed.
+ // Open the source in a new tab.
+ addNewTab("view-source:${currentWebView!!.currentUrl}", true)
+ }
+
+ // Consume the event.
+ true
+ }
+
+ R.id.view_headers -> { // View headers.
+ // Create an intent to launch the view headers activity.
+ val viewHeadersIntent = Intent(this, ViewHeadersActivity::class.java)
// Add the variables to the intent.
- viewSourceIntent.putExtra(CURRENT_URL, currentWebView!!.url)
- viewSourceIntent.putExtra(USER_AGENT, currentWebView!!.settings.userAgentString)
+ viewHeadersIntent.putExtra(CURRENT_URL, currentWebView!!.url)
+ viewHeadersIntent.putExtra(USER_AGENT, currentWebView!!.settings.userAgentString)
// Make it so.
- startActivity(viewSourceIntent)
+ startActivity(viewHeadersIntent)
// Consume the event.
true
// Load the previous website in the history.
currentWebView!!.goBack()
+
+ // Update the URL edit text after a delay.
+ updateUrlEditTextAfterDelay()
}
}
// Load the next website in the history.
currentWebView!!.goForward()
+
+ // Update the URL edit text after a delay.
+ updateUrlEditTextAfterDelay()
}
}
// Set a custom view on the new tab.
newTab.setCustomView(R.layout.tab_custom_view)
+ // Scroll to the new tab position.
+ tabLayout.post { tabLayout.setScrollPosition(newTabNumber, 0F, false, false) }
+
// Add the new WebView page.
- webViewPagerAdapter!!.addPage(newTabNumber, webViewPager, urlString, moveToTab)
+ webViewStateAdapter!!.addPage(newTabNumber, newTab, urlString, moveToTab)
// Show the app bar if it is at the bottom of the screen and the new tab is taking focus.
if (bottomAppBar && moveToTab && appBarLayout.translationY != 0f) {
}
private fun applyAppSettings() {
+ // Store the default preferences used in `applyDomainSettings()`. These are done here so that expensive preference requests are not done each time a domain is loaded.
+ 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)
+ defaultFanboysSocialBlockingList = sharedPreferences.getBoolean(getString(R.string.fanboys_social_blocking_list_key), true)
+ defaultUltraList = sharedPreferences.getBoolean(getString(R.string.ultralist_key), true)
+ defaultUltraPrivacy = sharedPreferences.getBoolean(getString(R.string.ultraprivacy_key), true)
+ defaultBlockAllThirdPartyRequests = sharedPreferences.getBoolean(getString(R.string.block_all_third_party_requests_key), false)
+ defaultFontSizeString = sharedPreferences.getString(getString(R.string.font_size_key), getString(R.string.font_size_default_value))!!
+ defaultUserAgentName = sharedPreferences.getString(getString(R.string.user_agent_key), getString(R.string.user_agent_default_value))!!
+ defaultSwipeToRefresh = sharedPreferences.getBoolean(getString(R.string.swipe_to_refresh_key), true)
+ defaultWebViewTheme = sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value))!!
+ 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.
+ webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values)
+
+ // Get the user agent array adapter and string array. These are done here so that expensive resource requests are not made each time a domain is loaded.
+ userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item)
+ userAgentDataArray = resources.getStringArray(R.array.user_agent_data)
+
// Store the values from the shared preferences in variables.
incognitoModeEnabled = sharedPreferences.getBoolean(getString(R.string.incognito_mode_key), false)
sanitizeTrackingQueries = sharedPreferences.getBoolean(getString(R.string.tracking_queries_key), true)
}
// Set the app bar scrolling for each WebView.
- for (i in 0 until webViewPagerAdapter!!.count) {
+ for (i in 0 until webViewStateAdapter!!.itemCount) {
// Get the WebView tab fragment.
- val webViewTabFragment = webViewPagerAdapter!!.getPageFragment(i)
+ val webViewTabFragment = webViewStateAdapter!!.getPageFragment(i)
// Get the fragment view.
val fragmentView = webViewTabFragment.view
newHostName = ""
// Apply the domain settings if a new domain is being loaded or if the new domain is blank. This allows the user to set temporary settings for JavaScript, cookies, DOM storage, etc.
- if (nestedScrollWebView.currentDomainName != newHostName || newHostName == "") {
+ if ((nestedScrollWebView.currentDomainName != newHostName) || (newHostName == "")) { // A new domain is being loaded.
// Set the new host name as the current domain name.
nestedScrollWebView.currentDomainName = newHostName
nestedScrollWebView.initializeFavoriteIcon()
// Get the current page position.
- val currentPagePosition = webViewPagerAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
+ val currentPagePosition = webViewStateAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
// Get the corresponding tab.
val tab = tabLayout.getTabAt(currentPagePosition)
newHostName = newHostName.substring(newHostName.indexOf(".") + 1)
}
- // Store the general preference information.
- val defaultFontSizeString = sharedPreferences.getString(getString(R.string.font_size_key), getString(R.string.font_size_default_value))
- val defaultUserAgentName = sharedPreferences.getString(getString(R.string.user_agent_key), getString(R.string.user_agent_default_value))
- val defaultSwipeToRefresh = sharedPreferences.getBoolean(getString(R.string.swipe_to_refresh_key), true)
- val webViewTheme = sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value))
- val wideViewport = sharedPreferences.getBoolean(getString(R.string.wide_viewport_key), true)
- val displayWebpageImages = sharedPreferences.getBoolean(getString(R.string.display_webpage_images_key), true)
-
- // Get the WebView theme entry values string array.
- val webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values)
-
- // Initialize the user agent array adapter and string array.
- val userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item)
- val userAgentDataArray = resources.getStringArray(R.array.user_agent_data)
-
- // Apply either the domain settings for the default settings.
+ // Apply either the domain settings or the default settings.
if (nestedScrollWebView.domainSettingsApplied) { // The url has custom domain settings.
// Get a cursor for the current host.
val currentDomainSettingsCursor = domainsDatabaseHelper!!.getCursorForDomainName(domainNameInDatabase!!)
currentDomainSettingsCursor.moveToFirst()
// Get the settings from the cursor.
- nestedScrollWebView.domainSettingsDatabaseId = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ID))
- nestedScrollWebView.settings.javaScriptEnabled = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1
- nestedScrollWebView.acceptCookies = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES)) == 1
- nestedScrollWebView.settings.domStorageEnabled = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1
- // Form data can be removed once the minimum API >= 26.
- val saveFormData = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1
- nestedScrollWebView.easyListEnabled = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1
- nestedScrollWebView.easyPrivacyEnabled = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1
- nestedScrollWebView.fanboysAnnoyanceListEnabled = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1
- nestedScrollWebView.fanboysSocialBlockingListEnabled =
- currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1
- nestedScrollWebView.ultraListEnabled = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST)) == 1
- nestedScrollWebView.ultraPrivacyEnabled = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)) == 1
- nestedScrollWebView.blockAllThirdPartyRequests = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1
- val userAgentName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT))
- val fontSize = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE))
- val swipeToRefreshInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH))
- val webViewThemeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME))
- val wideViewportInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WIDE_VIEWPORT))
- val displayWebpageImagesInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DISPLAY_IMAGES))
- val pinnedSslCertificate = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1
- val pinnedSslIssuedToCName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME))
- val pinnedSslIssuedToOName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION))
- val pinnedSslIssuedToUName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT))
- val pinnedSslIssuedByCName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME))
- val pinnedSslIssuedByOName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION))
- val pinnedSslIssuedByUName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT))
- val pinnedSslStartDate = Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_START_DATE)))
- val pinnedSslEndDate = Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_END_DATE)))
- val pinnedIpAddresses = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)) == 1
- val pinnedHostIpAddresses = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.IP_ADDRESSES))
+ nestedScrollWebView.domainSettingsDatabaseId = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ID))
+ 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 fanboysSocialBlockingListInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST))
+ val ultraListInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(com.stoutner.privacybrowser.helpers.ULTRALIST))
+ val ultraPrivacyInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(ENABLE_ULTRAPRIVACY))
+ val blockAllThirdPartyRequestsInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(com.stoutner.privacybrowser.helpers.BLOCK_ALL_THIRD_PARTY_REQUESTS))
+ val userAgentName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(com.stoutner.privacybrowser.helpers.USER_AGENT))
+ val fontSize = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(FONT_SIZE))
+ val swipeToRefreshInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(SWIPE_TO_REFRESH))
+ val webViewThemeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(WEBVIEW_THEME))
+ val wideViewportInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(WIDE_VIEWPORT))
+ val displayWebpageImagesInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DISPLAY_IMAGES))
+ val pinnedSslCertificate = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(PINNED_SSL_CERTIFICATE)) == 1)
+ val pinnedSslIssuedToCName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(SSL_ISSUED_TO_COMMON_NAME))
+ val pinnedSslIssuedToOName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(SSL_ISSUED_TO_ORGANIZATION))
+ val pinnedSslIssuedToUName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(SSL_ISSUED_TO_ORGANIZATIONAL_UNIT))
+ val pinnedSslIssuedByCName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(SSL_ISSUED_BY_COMMON_NAME))
+ val pinnedSslIssuedByOName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(SSL_ISSUED_BY_ORGANIZATION))
+ val pinnedSslIssuedByUName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(SSL_ISSUED_BY_ORGANIZATIONAL_UNIT))
+ val pinnedSslStartDate = Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndexOrThrow(com.stoutner.privacybrowser.helpers.SSL_START_DATE)))
+ val pinnedSslEndDate = Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndexOrThrow(com.stoutner.privacybrowser.helpers.SSL_END_DATE)))
+ val pinnedIpAddresses = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(PINNED_IP_ADDRESSES)) == 1)
+ val pinnedHostIpAddresses = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(IP_ADDRESSES))
// Close the current host domain settings cursor.
currentDomainSettingsCursor.close()
- // If there is a pinned SSL certificate, store it in the WebView.
- if (pinnedSslCertificate)
- nestedScrollWebView.setPinnedSslCertificate(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName,
- pinnedSslStartDate, pinnedSslEndDate)
+ // Set the JavaScript status.
+ when (javaScriptInt) {
+ SYSTEM_DEFAULT -> nestedScrollWebView.settings.javaScriptEnabled = defaultJavaScript
+ ENABLED -> nestedScrollWebView.settings.javaScriptEnabled = true
+ DISABLED -> nestedScrollWebView.settings.javaScriptEnabled = false
+ }
- // If there is a pinned IP address, store it in the WebView.
- if (pinnedIpAddresses)
- nestedScrollWebView.pinnedIpAddresses = pinnedHostIpAddresses
+ // Store the cookies status.
+ when (cookiesInt) {
+ SYSTEM_DEFAULT -> nestedScrollWebView.acceptCookies = defaultCookies
+ ENABLED -> nestedScrollWebView.acceptCookies = true
+ DISABLED -> nestedScrollWebView.acceptCookies = false
+ }
- // Apply the cookie domain settings.
+ // Apply the cookies status.
cookieManager.setAcceptCookie(nestedScrollWebView.acceptCookies)
+ // Set the DOM storage status.
+ when (domStorageInt) {
+ SYSTEM_DEFAULT -> nestedScrollWebView.settings.domStorageEnabled = defaultDomStorage
+ ENABLED -> nestedScrollWebView.settings.domStorageEnabled = true
+ DISABLED -> nestedScrollWebView.settings.domStorageEnabled = false
+ }
+
// Apply the form data setting if the API < 26.
@Suppress("DEPRECATION")
- if (Build.VERSION.SDK_INT < 26)
- nestedScrollWebView.settings.saveFormData = saveFormData
-
- // Apply the font size.
- try { // Try the specified font size to see if it is valid.
- if (fontSize == 0) { // Apply the default font size.
- // Set the font size from the value in the app settings.
- nestedScrollWebView.settings.textZoom = defaultFontSizeString!!.toInt()
- } else { // Apply the font size from domain settings.
- nestedScrollWebView.settings.textZoom = fontSize
+ 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
}
- } catch (exception: Exception) { // The specified font size is invalid
- // Set the font size to be 100%
- nestedScrollWebView.settings.textZoom = 100
+ }
+
+ // Set the EasyList status.
+ when (easyListInt) {
+ SYSTEM_DEFAULT -> nestedScrollWebView.easyListEnabled = defaultEasyList
+ ENABLED -> nestedScrollWebView.easyListEnabled = true
+ DISABLED -> nestedScrollWebView.easyListEnabled = false
+ }
+
+ // Set the EasyPrivacy status.
+ when (easyPrivacyInt) {
+ SYSTEM_DEFAULT -> nestedScrollWebView.easyPrivacyEnabled = defaultEasyPrivacy
+ ENABLED -> nestedScrollWebView.easyPrivacyEnabled = true
+ DISABLED -> nestedScrollWebView.easyPrivacyEnabled = false
+ }
+
+ // Set the Fanboy's Annoyance List status.
+ when (fanboysAnnoyanceListInt) {
+ SYSTEM_DEFAULT -> nestedScrollWebView.fanboysAnnoyanceListEnabled = defaultFanboysAnnoyanceList
+ ENABLED -> nestedScrollWebView.fanboysAnnoyanceListEnabled = true
+ DISABLED -> nestedScrollWebView.fanboysAnnoyanceListEnabled = false
+ }
+
+ // Set the Fanboy's Social Blocking List status.
+ when (fanboysSocialBlockingListInt) {
+ SYSTEM_DEFAULT -> nestedScrollWebView.fanboysSocialBlockingListEnabled = defaultFanboysSocialBlockingList
+ ENABLED -> nestedScrollWebView.fanboysSocialBlockingListEnabled = true
+ DISABLED -> nestedScrollWebView.fanboysSocialBlockingListEnabled = false
+ }
+
+ // Set the UltraList status.
+ when (ultraListInt) {
+ SYSTEM_DEFAULT -> nestedScrollWebView.ultraListEnabled = defaultUltraList
+ ENABLED -> nestedScrollWebView.ultraListEnabled = true
+ DISABLED -> nestedScrollWebView.ultraListEnabled = false
+ }
+
+ // Set the UltraPrivacy status.
+ when (ultraPrivacyInt) {
+ SYSTEM_DEFAULT -> nestedScrollWebView.ultraPrivacyEnabled = defaultUltraPrivacy
+ ENABLED -> nestedScrollWebView.ultraPrivacyEnabled = true
+ DISABLED -> nestedScrollWebView.ultraPrivacyEnabled = false
+ }
+
+ // Set the block all third-party requests status.
+ when (blockAllThirdPartyRequestsInt) {
+ SYSTEM_DEFAULT -> nestedScrollWebView.blockAllThirdPartyRequests = defaultBlockAllThirdPartyRequests
+ ENABLED -> nestedScrollWebView.blockAllThirdPartyRequests = true
+ DISABLED -> nestedScrollWebView.blockAllThirdPartyRequests = false
}
// Set the user agent.
}
}
+ // Apply the font size.
+ try { // Try the specified font size to see if it is valid.
+ if (fontSize == 0) { // Apply the default font size.
+ // Set the font size from the value in the app settings.
+ nestedScrollWebView.settings.textZoom = defaultFontSizeString.toInt()
+ } else { // Apply the font size from domain settings.
+ nestedScrollWebView.settings.textZoom = fontSize
+ }
+ } catch (exception: Exception) { // The specified font size is invalid
+ // Set the font size to be 100%
+ nestedScrollWebView.settings.textZoom = 100
+ }
+
// Set swipe to refresh.
when (swipeToRefreshInt) {
- DomainsDatabaseHelper.SYSTEM_DEFAULT -> {
+ SYSTEM_DEFAULT -> {
// Store the swipe to refresh status in the nested scroll WebView.
nestedScrollWebView.swipeToRefresh = defaultSwipeToRefresh
}
}
- DomainsDatabaseHelper.ENABLED -> {
+ ENABLED -> {
// Store the swipe to refresh status in the nested scroll WebView.
nestedScrollWebView.swipeToRefresh = true
}
}
- DomainsDatabaseHelper.DISABLED -> {
+ DISABLED -> {
// Store the swipe to refresh status in the nested scroll WebView.
nestedScrollWebView.swipeToRefresh = false
}
}
- // Set the WebView theme if device is running API >= 29 and algorithmic darkening is supported.
- if ((Build.VERSION.SDK_INT >= 29) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
+ // Set the WebView theme if algorithmic darkening is supported.
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
// Set the WebView theme.
when (webViewThemeInt) {
// Set the WebView theme.
- DomainsDatabaseHelper.SYSTEM_DEFAULT ->
- when (webViewTheme) {
+ SYSTEM_DEFAULT ->
+ when (defaultWebViewTheme) {
// The light theme is selected. Turn off algorithmic darkening.
webViewThemeEntryValuesStringArray[1] -> WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, false)
}
// Turn off algorithmic darkening.
- DomainsDatabaseHelper.LIGHT_THEME -> WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, false)
+ LIGHT_THEME -> WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, false)
// Turn on algorithmic darkening.
- DomainsDatabaseHelper.DARK_THEME -> WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, true)
+ DARK_THEME -> WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, true)
}
}
// Set the wide viewport status.
when (wideViewportInt) {
- DomainsDatabaseHelper.SYSTEM_DEFAULT -> nestedScrollWebView.settings.useWideViewPort = wideViewport
- DomainsDatabaseHelper.ENABLED -> nestedScrollWebView.settings.useWideViewPort = true
- DomainsDatabaseHelper.DISABLED -> nestedScrollWebView.settings.useWideViewPort = false
+ SYSTEM_DEFAULT -> nestedScrollWebView.settings.useWideViewPort = defaultWideViewport
+ ENABLED -> nestedScrollWebView.settings.useWideViewPort = true
+ DISABLED -> nestedScrollWebView.settings.useWideViewPort = false
}
// Set the display webpage images status.
when (displayWebpageImagesInt) {
- DomainsDatabaseHelper.SYSTEM_DEFAULT -> nestedScrollWebView.settings.loadsImagesAutomatically = displayWebpageImages
- DomainsDatabaseHelper.ENABLED -> nestedScrollWebView.settings.loadsImagesAutomatically = true
- DomainsDatabaseHelper.DISABLED -> nestedScrollWebView.settings.loadsImagesAutomatically = false
+ SYSTEM_DEFAULT -> nestedScrollWebView.settings.loadsImagesAutomatically = defaultDisplayWebpageImages
+ ENABLED -> nestedScrollWebView.settings.loadsImagesAutomatically = true
+ DISABLED -> nestedScrollWebView.settings.loadsImagesAutomatically = false
}
+ // If there is a pinned SSL certificate, store it in the WebView.
+ if (pinnedSslCertificate)
+ nestedScrollWebView.setPinnedSslCertificate(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName,
+ pinnedSslStartDate, pinnedSslEndDate)
+
+ // If there is a pinned IP address, store it in the WebView.
+ if (pinnedIpAddresses)
+ nestedScrollWebView.pinnedIpAddresses = pinnedHostIpAddresses
+
// Set a background on the URL relative layout to indicate that custom domain settings are being used.
urlRelativeLayout.background = AppCompatResources.getDrawable(this, R.drawable.domain_settings_url_background)
} else { // The new URL does not have custom domain settings. Load the defaults.
// Store the values from the shared preferences.
- nestedScrollWebView.settings.javaScriptEnabled = sharedPreferences.getBoolean(getString(R.string.javascript_key), false)
- nestedScrollWebView.acceptCookies = sharedPreferences.getBoolean(getString(R.string.cookies_key), false)
- nestedScrollWebView.settings.domStorageEnabled = sharedPreferences.getBoolean(getString(R.string.dom_storage_key), false)
- val saveFormData = sharedPreferences.getBoolean(getString(R.string.save_form_data_key), false) // Form data can be removed once the minimum API >= 26.
- nestedScrollWebView.easyListEnabled = sharedPreferences.getBoolean(getString(R.string.easylist_key), true)
- nestedScrollWebView.easyPrivacyEnabled = sharedPreferences.getBoolean(getString(R.string.easyprivacy_key), true)
- nestedScrollWebView.fanboysAnnoyanceListEnabled = sharedPreferences.getBoolean(getString(R.string.fanboys_annoyance_list_key), true)
- nestedScrollWebView.fanboysSocialBlockingListEnabled = sharedPreferences.getBoolean(getString(R.string.fanboys_social_blocking_list_key), true)
- nestedScrollWebView.ultraListEnabled = sharedPreferences.getBoolean(getString(R.string.ultralist_key), true)
- nestedScrollWebView.ultraPrivacyEnabled = sharedPreferences.getBoolean(getString(R.string.ultraprivacy_key), true)
- nestedScrollWebView.blockAllThirdPartyRequests = sharedPreferences.getBoolean(getString(R.string.block_all_third_party_requests_key), false)
+ nestedScrollWebView.settings.javaScriptEnabled = defaultJavaScript
+ nestedScrollWebView.acceptCookies = defaultCookies
+ nestedScrollWebView.settings.domStorageEnabled = defaultDomStorage
+ nestedScrollWebView.easyListEnabled = defaultEasyList
+ nestedScrollWebView.easyPrivacyEnabled = defaultEasyPrivacy
+ nestedScrollWebView.fanboysAnnoyanceListEnabled = defaultFanboysAnnoyanceList
+ nestedScrollWebView.fanboysSocialBlockingListEnabled = defaultFanboysSocialBlockingList
+ nestedScrollWebView.ultraListEnabled = defaultUltraList
+ nestedScrollWebView.ultraPrivacyEnabled = defaultUltraPrivacy
+ nestedScrollWebView.blockAllThirdPartyRequests = defaultBlockAllThirdPartyRequests
// 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.
- nestedScrollWebView.settings.textZoom = defaultFontSizeString!!.toInt()
+ nestedScrollWebView.settings.textZoom = defaultFontSizeString.toInt()
} catch (exception: Exception) {
// If the app settings value is invalid, set the font size to 100%.
nestedScrollWebView.settings.textZoom = 100
}
- // Apply the form data setting if the API < 26.
- if (Build.VERSION.SDK_INT < 26)
- @Suppress("DEPRECATION")
- nestedScrollWebView.settings.saveFormData = saveFormData
-
// Store the swipe to refresh status in the nested scroll WebView.
nestedScrollWebView.swipeToRefresh = defaultSwipeToRefresh
else -> nestedScrollWebView.settings.userAgentString = userAgentDataArray[userAgentArrayPosition]
}
- // Set the WebView theme if the device is running API >= 29 and algorithmic darkening is supported.
- if ((Build.VERSION.SDK_INT >= 29) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
+ // Set the WebView theme if algorithmic darkening is supported.
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
// Set the WebView theme.
- when (webViewTheme) {
+ when (defaultWebViewTheme) {
// The light theme is selected. Turn off algorithmic darkening.
webViewThemeEntryValuesStringArray[1] -> WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.settings, false)
}
// Set the viewport.
- nestedScrollWebView.settings.useWideViewPort = wideViewport
+ nestedScrollWebView.settings.useWideViewPort = defaultWideViewport
// Set the loading of webpage images.
- nestedScrollWebView.settings.loadsImagesAutomatically = displayWebpageImages
+ nestedScrollWebView.settings.loadsImagesAutomatically = defaultDisplayWebpageImages
// Set a transparent background on the URL relative layout.
urlRelativeLayout.background = AppCompatResources.getDrawable(this, R.color.transparent)
if (reloadWebsite)
nestedScrollWebView.reload()
+ // Disable the wide viewport if the source is being viewed.
+ if (url.startsWith("view-source:"))
+ nestedScrollWebView.settings.useWideViewPort = false
+
// Load the URL if directed. This makes sure that the domain settings are properly loaded before the URL. By using `loadUrl()`, instead of `loadUrlFromBase()`, the Referer header will never be sent.
if (loadUrl)
nestedScrollWebView.loadUrl(url)
// Reload the WebViews if requested and not waiting for the proxy.
if (reloadWebViews && !waitingForProxy) {
// Reload the WebViews.
- for (i in 0 until webViewPagerAdapter!!.count) {
+ for (i in 0 until webViewStateAdapter!!.itemCount) {
// Get the WebView tab fragment.
- val webViewTabFragment = webViewPagerAdapter!!.getPageFragment(i)
+ val webViewTabFragment = webViewStateAdapter!!.getPageFragment(i)
// Get the fragment view.
val fragmentView = webViewTabFragment.view
// The view parameter cannot be removed because it is called from the layout onClick.
fun bookmarksBack(@Suppress("UNUSED_PARAMETER")view: View?) {
- if (currentBookmarksFolder.isEmpty()) { // The home folder is displayed.
+ if (currentBookmarksFolderId == HOME_FOLDER_ID) { // The home folder is displayed.
// close the bookmarks drawer.
drawerLayout.closeDrawer(GravityCompat.END)
} else { // A subfolder is displayed.
// Set the former parent folder as the current folder.
- currentBookmarksFolder = bookmarksDatabaseHelper!!.getParentFolderName(currentBookmarksFolder)
+ currentBookmarksFolderId = bookmarksDatabaseHelper!!.getParentFolderId(currentBookmarksFolderId)
// Load the new folder.
loadBookmarksFolder()
// Clear the cache.
if (clearEverything || sharedPreferences.getBoolean(getString(R.string.clear_cache_key), true)) {
// Clear the cache from each WebView.
- for (i in 0 until webViewPagerAdapter!!.count) {
+ for (i in 0 until webViewStateAdapter!!.itemCount) {
// Get the WebView tab fragment.
- val webViewTabFragment = webViewPagerAdapter!!.getPageFragment(i)
+ val webViewTabFragment = webViewStateAdapter!!.getPageFragment(i)
// Get the WebView fragment view.
val webViewFragmentView = webViewTabFragment.view
}
// Wipe out each WebView.
- for (i in 0 until webViewPagerAdapter!!.count) {
+ for (i in 0 until webViewStateAdapter!!.itemCount) {
// Get the WebView tab fragment.
- val webViewTabFragment = webViewPagerAdapter!!.getPageFragment(i)
+ val webViewTabFragment = webViewStateAdapter!!.getPageFragment(i)
// Get the WebView frame layout.
val webViewFrameLayout = webViewTabFragment.view as FrameLayout?
// Delete the current page. If the selected page number did not change during the delete (because the newly selected tab has has same number as the previously deleted tab), it will return true,
// meaning that the current WebView must be reset. Otherwise it will happen automatically as the selected tab number changes.
- if (webViewPagerAdapter!!.deletePage(currentTabNumber, webViewPager))
+ if (webViewStateAdapter!!.deletePage(currentTabNumber, webViewViewPager2))
setCurrentWebView(currentTabNumber)
} else { // There is only one tab open.
clearAndExit()
val newBookmarkDisplayOrder = bookmarksListView.count
// Create the bookmark.
- bookmarksDatabaseHelper!!.createBookmark(bookmarkNameString, bookmarkUrlString, currentBookmarksFolder, newBookmarkDisplayOrder, favoriteIconByteArray)
+ bookmarksDatabaseHelper!!.createBookmark(bookmarkNameString, bookmarkUrlString, currentBookmarksFolderId, newBookmarkDisplayOrder, favoriteIconByteArray)
// Update the bookmarks cursor with the current contents of this folder.
- bookmarksCursor = bookmarksDatabaseHelper!!.getBookmarksByDisplayOrder(currentBookmarksFolder)
+ bookmarksCursor = bookmarksDatabaseHelper!!.getBookmarksByDisplayOrder(currentBookmarksFolderId)
// Update the list view.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
}
// Create the folder, which will be placed at the top of the list view.
- bookmarksDatabaseHelper!!.createFolder(folderNameString, currentBookmarksFolder, folderIconByteArray)
+ bookmarksDatabaseHelper!!.createFolder(folderNameString, currentBookmarksFolderId, folderIconByteArray)
// Update the bookmarks cursor with the current contents of this folder.
- bookmarksCursor = bookmarksDatabaseHelper!!.getBookmarksByDisplayOrder(currentBookmarksFolder)
+ bookmarksCursor = bookmarksDatabaseHelper!!.getBookmarksByDisplayOrder(currentBookmarksFolderId)
// Update the list view.
bookmarksCursorAdapter.changeCursor(bookmarksCursor)
// Check to see if the activity has been restarted with a saved state.
if ((savedStateArrayList == null) || (savedStateArrayList!!.size == 0)) { // The activity has not been restarted or it was restarted on start to change the theme.
// Add the first tab.
- addNewTab("", true)
- } else { // The activity has been restarted.
+ addNewTab("", false)
+ } else { // The activity has been restarted with a saved state.
+ // Set the current restart time.
+ restartTime = Date()
+
// Restore each tab.
for (i in savedStateArrayList!!.indices) {
// Add a new tab.
newTab.setCustomView(R.layout.tab_custom_view)
// Add the new page.
- webViewPagerAdapter!!.restorePage(savedStateArrayList!![i], savedNestedScrollWebViewStateArrayList!![i])
+ webViewStateAdapter!!.restorePage(savedStateArrayList!![i], savedNestedScrollWebViewStateArrayList!![i])
}
// Reset the saved state variables.
// Set the first page as the current WebView.
setCurrentWebView(0)
} else { // The first tab is not selected.
- // Move to the selected tab.
- webViewPager.currentItem = savedTabPosition
+ // Select the tab when the layout has finished populating.
+ tabLayout.post {
+ // Get a handle for the tab.
+ val tab = tabLayout.getTabAt(savedTabPosition)!!
+
+ // Select the tab.
+ tab.select()
+ }
}
// Get the intent that started the app.
}
// Reload existing URLs and load any URLs that are waiting for the proxy.
- for (i in 0 until webViewPagerAdapter!!.count) {
+ for (i in 0 until webViewStateAdapter!!.itemCount) {
// Get the WebView tab fragment.
- val webViewTabFragment = webViewPagerAdapter!!.getPageFragment(i)
+ val webViewTabFragment = webViewStateAdapter!!.getPageFragment(i)
// Get the fragment view.
val fragmentView = webViewTabFragment.view
val createBookmarkFolderFab = findViewById<FloatingActionButton>(R.id.create_bookmark_folder_fab)
val createBookmarkFab = findViewById<FloatingActionButton>(R.id.create_bookmark_fab)
- // Update the WebView pager every time a tab is modified.
- webViewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
- override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
-
- override fun onPageSelected(position: Int) {
+ // Handle tab selections.
+ tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
+ override fun onTabSelected(tab: TabLayout.Tab) {
// Close the find on page bar if it is open.
closeFindOnPage(null)
- // Set the current WebView.
- setCurrentWebView(position)
-
- // Select the corresponding tab if it does not match the currently selected page. This will happen if the page was scrolled by creating a new tab.
- if (tabLayout.selectedTabPosition != position) {
- // Wait until the new tab has been created.
- tabLayout.post {
- // Get a handle for the tab.
- val tab = tabLayout.getTabAt(position)!!
+ // Update the view pager when it has quiesced. Otherwise, if a page launched by a new intent on restart has not yet been created, the view pager will not be updated to match the tab layout.
+ webViewViewPager2.post {
+ // Select the same page in the view pager.
+ webViewViewPager2.currentItem = tab.position
- // Select the tab.
- tab.select()
- }
+ // Set the current WebView.
+ setCurrentWebView(tab.position)
}
}
- override fun onPageScrollStateChanged(state: Int) {}
- })
-
- // Handle tab selections.
- tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
- override fun onTabSelected(tab: TabLayout.Tab) {
- // Select the same page in the view pager.
- webViewPager.currentItem = tab.position
- }
-
override fun onTabUnselected(tab: TabLayout.Tab) {}
override fun onTabReselected(tab: TabLayout.Tab) {
- // Instantiate the View SSL Certificate dialog.
- val viewSslCertificateDialogFragment: DialogFragment = ViewSslCertificateDialog.displayDialog(currentWebView!!.webViewFragmentId, currentWebView!!.getFavoriteIcon())
-
- // Display the View SSL Certificate dialog.
- viewSslCertificateDialogFragment.show(supportFragmentManager, getString(R.string.view_ssl_certificate))
+ // Only display the view SSL certificate dialog if the current WebView is not null.
+ // This can happen if the tab is programmatically reselected while the app is being restarted and is not yet populated.
+ if (currentWebView != null) {
+ // Calculate the milliseconds since the last restart. This can be replaced by the simpler LocalDateTime once the minimum API >= 26.
+ val millisecondsSinceLastRestart = Date().time - restartTime.time
+
+ // Only display the SSL certificate dialog if it has been at least 3 seconds since the last restart as deep restarts sometimes end up selecting a tab twice.
+ if (millisecondsSinceLastRestart > 3000) {
+ // Instantiate the View SSL Certificate dialog.
+ val viewSslCertificateDialogFragment: DialogFragment = ViewSslCertificateDialog.displayDialog(currentWebView!!.webViewFragmentId, currentWebView!!.getFavoriteIcon())
+
+ // Display the View SSL Certificate dialog.
+ viewSslCertificateDialogFragment.show(supportFragmentManager, getString(R.string.view_ssl_certificate))
+ }
+ }
}
})
val bookmarksIntent = Intent(applicationContext, BookmarksActivity::class.java)
// Add the extra information to the intent.
- bookmarksIntent.putExtra(CURRENT_FOLDER, currentBookmarksFolder)
+ bookmarksIntent.putExtra(CURRENT_FOLDER_ID, currentBookmarksFolderId)
bookmarksIntent.putExtra(CURRENT_TITLE, currentWebView!!.title)
bookmarksIntent.putExtra(CURRENT_URL, currentWebView!!.url)
bookmarksIntent.putExtra(CURRENT_FAVORITE_ICON_BYTE_ARRAY, currentFavoriteIconByteArray)
bookmarkCursor.moveToFirst()
// Act upon the bookmark according to the type.
- if (bookmarkCursor.getInt(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)) == 1) { // The selected bookmark is a folder.
- // Store the folder name.
- currentBookmarksFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME))
+ if (bookmarkCursor.getInt(bookmarkCursor.getColumnIndexOrThrow(IS_FOLDER)) == 1) { // The selected bookmark is a folder.
+ // Store the folder ID.
+ currentBookmarksFolderId = bookmarkCursor.getLong(bookmarkCursor.getColumnIndexOrThrow(FOLDER_ID))
// Load the new folder.
loadBookmarksFolder()
} else { // The selected bookmark is not a folder.
// Load the bookmark URL.
- loadUrl(currentWebView!!, bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)))
+ loadUrl(currentWebView!!, bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BOOKMARK_URL)))
// Close the bookmarks drawer if it is not pinned.
if (!bookmarksDrawerPinned)
// Run the commands associated with the type.
if (bookmarksDatabaseHelper!!.isFolder(databaseId)) { // The bookmark is a folder.
+ // Get the folder ID.
+ val folderId = bookmarksDatabaseHelper!!.getFolderId(databaseId)
+
// Get a cursor of all the bookmarks in the folder.
- val bookmarksCursor = bookmarksDatabaseHelper!!.getFolderBookmarks(databaseId)
+ val bookmarksCursor = bookmarksDatabaseHelper!!.getFolderBookmarks(folderId)
// Move to the first entry in the cursor.
bookmarksCursor.moveToFirst()
// Open each bookmark
for (i in 0 until bookmarksCursor.count) {
// Load the bookmark in a new tab, moving to the tab for the first bookmark if the drawer is not pinned.
- addNewTab(bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)), !bookmarksDrawerPinned && (i == 0))
+ addNewTab(bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BOOKMARK_URL)), !bookmarksDrawerPinned && (i == 0))
// Move to the next bookmark.
bookmarksCursor.moveToNext()
bookmarkCursor.moveToFirst()
// Load the bookmark in a new tab and move to the tab if the drawer is not pinned.
- addNewTab(bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)), !bookmarksDrawerPinned)
+ addNewTab(bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BOOKMARK_URL)), !bookmarksDrawerPinned)
// Close the cursor.
bookmarkCursor.close()
// Clear the focus from from the WebView if it is not null, which can happen if a user opens a drawer while the browser is being resumed.
// Clearing the focus from the WebView removes any text selection markers and context menus, which otherwise draw above the open drawers.
currentWebView?.clearFocus()
+
+ if (bottomAppBar && navigationDrawerFirstView) {
+ // Reset the navigation drawer first view flag.
+ navigationDrawerFirstView = false
+
+ // Get a handle for the navigation recycler view.
+ val navigationRecyclerView = navigationView.getChildAt(0) as RecyclerView
+
+ // Get the navigation linear layout manager.
+ val navigationLinearLayoutManager = navigationRecyclerView.layoutManager as LinearLayoutManager
+
+ // Scroll the navigation drawer to the bottom.
+ navigationLinearLayoutManager.scrollToPositionWithOffset(13, 0)
+ }
}
}
})
// Get the WebView theme entry values string array.
val webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values)
- // Set the WebView theme if device is running API >= 29 and algorithmic darkening is supported.
- if (Build.VERSION.SDK_INT >= 29 && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
+ // Set the WebView theme if algorithmic darkening is supported.
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
// Set the WebView them. A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
if (webViewTheme == webViewThemeEntryValuesStringArray[1]) { // The light theme is selected.
// Turn off algorithmic darkening.
nestedScrollWebView.setFavoriteIcon(icon)
// Get the current page position.
- val currentPosition = webViewPagerAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
+ val currentPosition = webViewStateAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
// Get the current tab.
val tab = tabLayout.getTabAt(currentPosition)
// Save a copy of the title when it changes.
override fun onReceivedTitle(view: WebView, title: String) {
// Get the current page position.
- val currentPosition = webViewPagerAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
+ val currentPosition = webViewStateAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
// Get the current tab.
val tab = tabLayout.getTabAt(currentPosition)
}
// Get the current WebView page position.
- val webViewPagePosition = webViewPagerAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
+ val webViewPagePosition = webViewStateAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
// Determine if the WebView is currently displayed.
- val webViewDisplayed = webViewPagePosition == tabLayout.selectedTabPosition
+ val webViewDisplayed = (webViewPagePosition == tabLayout.selectedTabPosition)
// Block third-party requests if enabled.
if (isThirdPartyRequest && nestedScrollWebView.blockAllThirdPartyRequests) {
// Increment the blocked requests counters.
nestedScrollWebView.incrementRequestsCount(BLOCKED_REQUESTS)
- nestedScrollWebView.incrementRequestsCount(ULTRALIST)
+ nestedScrollWebView.incrementRequestsCount(com.stoutner.privacybrowser.views.ULTRALIST)
// Update the titles of the filter lists menu items if the WebView is currently displayed.
if (webViewDisplayed) {
// Update the options menu if it has been populated.
if (optionsMenu != null) {
optionsFilterListsMenuItem.title = getString(R.string.filterlists) + " - " + nestedScrollWebView.getRequestsCount(BLOCKED_REQUESTS)
- optionsUltraListMenuItem.title = nestedScrollWebView.getRequestsCount(ULTRALIST).toString() + " - " + getString(R.string.ultralist)
+ optionsUltraListMenuItem.title = nestedScrollWebView.getRequestsCount(com.stoutner.privacybrowser.views.ULTRALIST).toString() + " - " + getString(R.string.ultralist)
}
}
}
nestedScrollWebView.resetRequestsCounters()
// Get the current page position.
- val currentPagePosition = webViewPagerAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
+ val currentPagePosition = webViewStateAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
// Update the URL text bar if the page is currently selected and the URL edit text is not currently being edited.
if ((tabLayout.selectedTabPosition == currentPagePosition) && !urlEditText.hasFocus()) {
- // Display the formatted URL text.
- urlEditText.setText(url)
+ // Display the formatted URL text. The nested scroll WebView current URL preserves any initial `view-source:`, and opposed to the method URL variable.
+ urlEditText.setText(nestedScrollWebView.currentUrl)
// Highlight the URL syntax.
UrlHelper.highlightSyntax(urlEditText, initialGrayColorSpan, finalGrayColorSpan, redColorSpan)
val currentDomainName = currentUri.host
// Get the IP addresses for the current domain.
- if ((currentDomainName != null) && currentDomainName.isNotEmpty())
+ if (!currentDomainName.isNullOrEmpty())
GetHostIpAddressesCoroutine.checkPinnedMismatch(currentDomainName, nestedScrollWebView, supportFragmentManager, getString(R.string.pinned_mismatch))
// Replace Refresh with Stop if the options menu has been created and the WebView is currently displayed. (The first WebView typically begins loading before the menu items are instantiated.)
}
// Get the current page position.
- val currentPagePosition = webViewPagerAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
+ val currentPagePosition = webViewStateAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
// Get the current URL from the nested scroll WebView. This is more accurate than using the URL passed into the method, which is sometimes not the final one.
val currentUrl = nestedScrollWebView.url
private fun loadBookmarksFolder() {
// Update the bookmarks cursor with the contents of the bookmarks database for the current folder.
- bookmarksCursor = bookmarksDatabaseHelper!!.getBookmarksByDisplayOrder(currentBookmarksFolder)
+ bookmarksCursor = bookmarksDatabaseHelper!!.getBookmarksByDisplayOrder(currentBookmarksFolderId)
// Populate the bookmarks cursor adapter.
bookmarksCursorAdapter = object : CursorAdapter(this, bookmarksCursor, false) {
val bookmarkNameTextView = view.findViewById<TextView>(R.id.bookmark_name)
// Get the favorite icon byte array from the cursor.
- val favoriteIconByteArray = cursor.getBlob(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON))
+ val favoriteIconByteArray = cursor.getBlob(cursor.getColumnIndexOrThrow(FAVORITE_ICON))
// Convert the byte array to a bitmap beginning at the first byte and ending at the last.
val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
bookmarkFavoriteIcon.setImageBitmap(favoriteIconBitmap)
// Display the bookmark name from the cursor in the bookmark name text view.
- bookmarkNameTextView.text = cursor.getString(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME))
+ bookmarkNameTextView.text = cursor.getString(cursor.getColumnIndexOrThrow(BOOKMARK_NAME))
// Make the font bold for folders.
- if (cursor.getInt(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)) == 1)
+ if (cursor.getInt(cursor.getColumnIndexOrThrow(IS_FOLDER)) == 1)
bookmarkNameTextView.typeface = Typeface.DEFAULT_BOLD
else // Reset the font to default for normal bookmarks.
bookmarkNameTextView.typeface = Typeface.DEFAULT
bookmarksListView.adapter = bookmarksCursorAdapter
// Set the bookmarks drawer title.
- if (currentBookmarksFolder.isEmpty())
+ if (currentBookmarksFolderId == HOME_FOLDER_ID) // The current bookmarks folder is the home folder.
bookmarksTitleTextView.setText(R.string.bookmarks)
else
- bookmarksTitleTextView.text = currentBookmarksFolder
+ bookmarksTitleTextView.text = bookmarksDatabaseHelper!!.getFolderName(currentBookmarksFolderId)
}
private fun loadUrl(nestedScrollWebView: NestedScrollWebView, url: String) {
var urlString = ""
// Check to see if the unformatted URL string is a valid URL. Otherwise, convert it into a search.
- if (unformattedUrlString.startsWith("content://")) { // This is a content URL.
+ if (unformattedUrlString.startsWith("content://") || unformattedUrlString.startsWith("view-source:")) { // This is a content or source URL.
// Load the entire content URL.
urlString = unformattedUrlString
} else if (Patterns.WEB_URL.matcher(unformattedUrlString).matches() || unformattedUrlString.startsWith("http://") || unformattedUrlString.startsWith("https://") ||
// Load the history entry.
currentWebView!!.goBackOrForward(steps)
+
+ // Update the URL edit text after a delay.
+ updateUrlEditTextAfterDelay()
}
override fun openFile(dialogFragment: DialogFragment) {
// Go back.
currentWebView!!.goBack()
+
+ // Update the URL edit text after a delay.
+ updateUrlEditTextAfterDelay()
}
private fun sanitizeUrl(urlString: String): String {
// Stop the swipe to refresh indicator if it is running
swipeRefreshLayout.isRefreshing = false
- // Get the WebView tab fragment.
- val webViewTabFragment = webViewPagerAdapter!!.getPageFragment(pageNumber)
+ // Try to set the current WebView. This will fail if the WebView has not yet been populated.
+ try {
+ // Get the WebView tab fragment.
+ val webViewTabFragment = webViewStateAdapter!!.getPageFragment(pageNumber)
- // Get the fragment view.
- val webViewFragmentView = webViewTabFragment.view
+ // Get the fragment view.
+ val webViewFragmentView = webViewTabFragment.view
- // Set the current WebView if the fragment view is not null.
- if (webViewFragmentView != null) { // The fragment has been populated.
// Store the current WebView.
- currentWebView = webViewFragmentView.findViewById(R.id.nestedscroll_webview)
+ currentWebView = webViewFragmentView!!.findViewById(R.id.nestedscroll_webview)
// Update the status of swipe to refresh.
if (currentWebView!!.swipeToRefresh) { // Swipe to refresh is enabled.
// Remove any background on the URL relative layout.
urlRelativeLayout.background = AppCompatResources.getDrawable(this, R.color.transparent)
}
- } else if (pageNumber == savedTabPosition) { // The app is being restored but the saved tab position fragment has not been populated yet. Try again in 100 milliseconds.
+ } catch (exception: Exception) {
+ // Try again in 100 milliseconds if the WebView has not yet been populated.
// Create a handler to set the current WebView.
val setCurrentWebViewHandler = Handler(Looper.getMainLooper())
setCurrentWebView(pageNumber)
}
- // Try setting the current WebView again after 100 milliseconds.
- setCurrentWebViewHandler.postDelayed(setCurrentWebWebRunnable, 100)
+ // Try setting the current WebView again after 50 milliseconds.
+ setCurrentWebViewHandler.postDelayed(setCurrentWebWebRunnable, 50)
}
}
val domainsCount = domainsCursor.count
// Get the domain name column index.
- val domainNameColumnIndex = domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME)
+ val domainNameColumnIndex = domainsCursor.getColumnIndexOrThrow(DOMAIN_NAME)
// Populate the domain settings set.
for (i in 0 until domainsCount) {
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)
+ }
}