+ // Set the app bar scrolling.
+ nestedScrollWebView.isNestedScrollingEnabled = scrollAppBar
+
+ // Allow pinch to zoom.
+ nestedScrollWebView.settings.builtInZoomControls = true
+
+ // Hide zoom controls.
+ nestedScrollWebView.settings.displayZoomControls = false
+
+ // Don't allow mixed content (HTTP and HTTPS) on the same website.
+ nestedScrollWebView.settings.mixedContentMode = WebSettings.MIXED_CONTENT_NEVER_ALLOW
+
+ // Set the WebView to load in overview mode (zoomed out to the maximum width).
+ nestedScrollWebView.settings.loadWithOverviewMode = true
+
+ // Explicitly disable geolocation.
+ nestedScrollWebView.settings.setGeolocationEnabled(false)
+
+ // Allow loading of file:// URLs. This is necessary for opening MHT web archives, which are copied into a temporary cache location.
+ nestedScrollWebView.settings.allowFileAccess = true
+
+ // Create a double-tap gesture detector to toggle full-screen mode.
+ val doubleTapGestureDetector = GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() {
+ // Override `onDoubleTap()`. All other events are handled using the default settings.
+ override fun onDoubleTap(motionEvent: MotionEvent): Boolean {
+ return if (fullScreenBrowsingModeEnabled) { // Only process the double-tap if full screen browsing mode is enabled.
+ // Toggle the full screen browsing mode tracker.
+ inFullScreenBrowsingMode = !inFullScreenBrowsingMode
+
+ // Toggle the full screen browsing mode.
+ if (inFullScreenBrowsingMode) { // Switch to full screen mode.
+ // Hide the app bar if specified.
+ if (hideAppBar) { // App bar hiding is enabled.
+ // Close the find on page bar if it is visible.
+ closeFindOnPage(null)
+
+ // Hide the tab linear layout.
+ tabsLinearLayout.visibility = View.GONE
+
+ // Hide the app bar.
+ appBar.hide()
+
+ // Set layout and scrolling parameters according to the position of the app bar.
+ if (bottomAppBar) { // The app bar is at the bottom.
+ // Reset the WebView padding to fill the available space.
+ swipeRefreshLayout.setPadding(0, 0, 0, 0)
+ } else { // The app bar is at the top.
+ // Check to see if the app bar is normally scrolled.
+ if (scrollAppBar) { // The app bar is scrolled when it is displayed.
+ // Get the swipe refresh layout parameters.
+ val swipeRefreshLayoutParams = swipeRefreshLayout.layoutParams as CoordinatorLayout.LayoutParams
+
+ // Remove the off-screen scrolling layout.
+ swipeRefreshLayoutParams.behavior = null
+ } else { // The app bar is not scrolled when it is displayed.
+ // Remove the padding from the top of the swipe refresh layout.
+ swipeRefreshLayout.setPadding(0, 0, 0, 0)
+
+ // The swipe refresh circle must be moved above the now removed status bar location.
+ swipeRefreshLayout.setProgressViewOffset(false, -200, defaultProgressViewEndOffset)
+ }
+ }
+ } else { // App bar hiding is not enabled.
+ // Adjust the UI for the bottom app bar.
+ if (bottomAppBar) {
+ // Adjust the UI according to the scrolling of the app bar.
+ if (scrollAppBar) {
+ // Reset the WebView padding to fill the available space.
+ swipeRefreshLayout.setPadding(0, 0, 0, 0)
+ } else {
+ // Move the WebView above the app bar layout.
+ swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight)
+ }
+ }
+ }
+
+ /* Hide the system bars.
+ * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
+ * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
+ * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
+ * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
+ */
+
+ // The deprecated command can be switched to `WindowInsetsController` once the minimum API >= 30.
+ @Suppress("DEPRECATION")
+ rootFrameLayout.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ } else { // Switch to normal viewing mode.
+ // Show the app bar if it was hidden.
+ if (hideAppBar) {
+ // Show the tab linear layout.
+ tabsLinearLayout.visibility = View.VISIBLE
+
+ // Show the app bar.
+ appBar.show()
+ }
+
+ // Set layout and scrolling parameters according to the position of the app bar.
+ if (bottomAppBar) { // The app bar is at the bottom.
+ // Adjust the UI.
+ if (scrollAppBar) {
+ // Reset the WebView padding to fill the available space.
+ swipeRefreshLayout.setPadding(0, 0, 0, 0)
+ } else {
+ // Move the WebView above the app bar layout.
+ swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight)
+ }
+ } else { // The app bar is at the top.
+ // Check to see if the app bar is normally scrolled.
+ if (scrollAppBar) { // The app bar is scrolled when it is displayed.
+ // Get the swipe refresh layout parameters.
+ val swipeRefreshLayoutParams = swipeRefreshLayout.layoutParams as CoordinatorLayout.LayoutParams
+
+ // Add the off-screen scrolling layout.
+ swipeRefreshLayoutParams.behavior = AppBarLayout.ScrollingViewBehavior()
+ } else { // The app bar is not scrolled when it is displayed.
+ // The swipe refresh layout must be manually moved below the app bar layout.
+ swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0)
+
+ // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels.
+ swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight)
+ }
+ }
+
+ // Remove the `SYSTEM_UI` flags from the root frame layout. The deprecated command can be switched to `WindowInsetsController` once the minimum API >= 30.
+ @Suppress("DEPRECATION")
+ rootFrameLayout.systemUiVisibility = 0
+ }
+
+ // Consume the double-tap.
+ true
+ } else { // Do not consume the double-tap because full screen browsing mode is disabled.
+ // Return false.
+ false
+ }
+ }
+
+ override fun onFling(motionEvent1: MotionEvent?, motionEvent2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
+ // Scroll the bottom app bar if enabled.
+ if (bottomAppBar && scrollAppBar && !objectAnimator.isRunning && (motionEvent1 != null)) {
+ // Calculate the Y change.
+ val motionY = motionEvent2.y - motionEvent1.y
+
+ // Scroll the app bar if the change is greater than 50 pixels.
+ if (motionY > 50) {
+ // Animate the bottom app bar onto the screen.
+ objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", 0f)
+ } else if (motionY < -50) {
+ // Animate the bottom app bar off the screen.
+ objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", appBarLayout.height.toFloat())
+ }
+
+ // Make it so.
+ objectAnimator.start()
+ }
+
+ // Do not consume the event.
+ return false
+ }
+ })
+
+ // Pass all touch events on the WebView through the double-tap gesture detector.
+ nestedScrollWebView.setOnTouchListener { view: View, motionEvent: MotionEvent? ->
+ // Call `performClick()` on the view, which is required for accessibility.
+ view.performClick()
+
+ // Check for double-taps.
+ doubleTapGestureDetector.onTouchEvent(motionEvent!!)