2 * Copyright 2019-2023 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
6 * Privacy Browser Android is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * Privacy Browser Android is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Browser Android. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacybrowser.views
22 import android.animation.ObjectAnimator
23 import android.annotation.SuppressLint
24 import android.content.Context
25 import android.graphics.Bitmap
26 import android.graphics.drawable.BitmapDrawable
27 import android.graphics.drawable.Drawable
28 import android.os.Bundle
29 import android.util.AttributeSet
30 import android.view.MotionEvent
31 import android.webkit.HttpAuthHandler
32 import android.webkit.SslErrorHandler
33 import android.webkit.WebView
35 import androidx.appcompat.content.res.AppCompatResources.getDrawable
36 import androidx.core.view.NestedScrollingChild2
37 import androidx.core.view.NestedScrollingChildHelper
38 import androidx.core.view.ViewCompat
40 import com.stoutner.privacybrowser.R
41 import com.stoutner.privacybrowser.activities.MainWebViewActivity
43 import java.util.Collections
46 import kotlin.collections.ArrayList
48 // Define the public constants.
49 const val BLOCKED_REQUESTS = 0
50 const val EASYLIST = 1
51 const val EASYPRIVACY = 2
52 const val FANBOYS_ANNOYANCE_LIST = 3
53 const val FANBOYS_SOCIAL_BLOCKING_LIST = 4
54 const val ULTRALIST = 5
55 const val ULTRAPRIVACY = 6
56 const val THIRD_PARTY_REQUESTS = 7
58 // Define the private class constants.
59 private const val ACCEPT_COOKIES = "A"
60 private const val BLOCK_ALL_THIRD_PARTY_REQUESTS = "B"
61 private const val CURRENT_DOMAIN_NAME = "C"
62 private const val CURRENT_URL = "D"
63 private const val DOM_STORAGE_ENABLED = "E"
64 private const val DOMAIN_SETTINGS_APPLIED = "F"
65 private const val DOMAIN_SETTINGS_DATABASE_ID = "G"
66 private const val EASYLIST_ENABLED = "H"
67 private const val EASYPRIVACY_ENABLED = "I"
68 private const val FANBOYS_ANNOYANCE_LIST_ENABLED = "J"
69 private const val FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED = "K"
70 private const val FONT_SIZE = "L"
71 private const val HAS_PINNED_SSL_CERTIFICATE = "M"
72 private const val IGNORE_PINNED_DOMAIN_INFORMATION = "N"
73 private const val JAVASCRIPT_ENABLED = "O"
74 private const val PINNED_IP_ADDRESSES = "P"
75 private const val PINNED_SSL_END_DATE = "Q"
76 private const val PINNED_SSL_ISSUED_BY_CNAME = "R"
77 private const val PINNED_SSL_ISSUED_BY_ONAME = "S"
78 private const val PINNED_SSL_ISSUED_BY_UNAME = "T"
79 private const val PINNED_SSL_ISSUED_TO_CNAME = "U"
80 private const val PINNED_SSL_ISSUED_TO_ONAME = "V"
81 private const val PINNED_SSL_ISSUED_TO_UNAME = "W"
82 private const val PINNED_SSL_START_DATE = "X"
83 private const val SWIPE_TO_REFRESH = "Y"
84 private const val ULTRALIST_ENABLED = "Z"
85 private const val ULTRAPRIVACY_ENABLED = "AA"
86 private const val USER_AGENT = "AB"
87 private const val WIDE_VIEWPORT = "AC"
89 // NestedScrollWebView extends WebView to handle nested scrolls (scrolling the app bar off the screen). It also stores extra information about the state of the WebView used by Privacy Browser.
90 class NestedScrollWebView @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null, defaultStyle: Int = android.R.attr.webViewStyle) : WebView(context, attributeSet, defaultStyle),
91 NestedScrollingChild2 {
93 // Define the public variables.
94 var acceptCookies = false
95 var blockAllThirdPartyRequests = false
96 var currentDomainName = ""
97 var currentIpAddresses = ""
99 var domainSettingsApplied = false
100 var domainSettingsDatabaseId = 0
101 var easyListEnabled = true
102 var easyPrivacyEnabled = true
103 var fanboysAnnoyanceListEnabled = true
104 var fanboysSocialBlockingListEnabled = true
105 var httpAuthHandler: HttpAuthHandler? = null
106 var ignorePinnedDomainInformation = false
107 var pinnedIpAddresses = ""
108 var previousFavoriteIconDrawable: Drawable? = null
109 var previousWebpageTitle = ""
110 var sslErrorHandler: SslErrorHandler? = null
111 var swipeToRefresh = false
112 var ultraListEnabled = true
113 var ultraPrivacyEnabled = true
114 var waitingForProxyUrlString = ""
115 var webViewFragmentId: Long = 0
117 // Define the private variables.
118 private val nestedScrollingChildHelper: NestedScrollingChildHelper = NestedScrollingChildHelper(this)
119 private lateinit var favoriteIcon: Bitmap
120 private var favoriteIconHeight = 0
121 private var previousYPosition = 0 // The previous Y position needs to be tracked between motion events.
122 private var hasPinnedSslCertificate = false
123 private var pinnedSslIssuedToCName = ""
124 private var pinnedSslIssuedToOName = ""
125 private var pinnedSslIssuedToUName = ""
126 private var pinnedSslIssuedByCName = ""
127 private var pinnedSslIssuedByOName = ""
128 private var pinnedSslIssuedByUName = ""
129 private var pinnedSslStartDate = Date(0)
130 private var pinnedSslEndDate = Date(0)
131 private val resourceRequests = Collections.synchronizedList(ArrayList<Array<String>>()) // Using a synchronized list makes adding resource requests thread safe.
132 private var blockedRequests = 0
133 private var easyListBlockedRequests = 0
134 private var easyPrivacyBlockedRequests = 0
135 private var fanboysAnnoyanceListBlockedRequests = 0
136 private var fanboysSocialBlockingListBlockedRequests = 0
137 private var ultraListBlockedRequests = 0
138 private var ultraPrivacyBlockedRequests = 0
139 private var thirdPartyBlockedRequests = 0
142 // Enable nested scrolling by default.
143 nestedScrollingChildHelper.isNestedScrollingEnabled = true
145 // Initialize the favorite icon.
146 initializeFavoriteIcon()
149 // Favorite or default icon.
150 fun initializeFavoriteIcon() {
151 // Get the default favorite icon drawable.
152 val favoriteIconDrawable = getDrawable(context, R.drawable.world)
154 // Cast the favorite icon drawable to a bitmap drawable.
155 val favoriteIconBitmapDrawable = (favoriteIconDrawable as BitmapDrawable?)!!
157 // Store the default icon bitmap.
158 favoriteIcon = favoriteIconBitmapDrawable.bitmap
160 // Set the favorite icon height to be 0. This way any favorite icons presented by the website will overwrite it.
161 favoriteIconHeight = 0
164 fun setFavoriteIcon(icon: Bitmap) {
165 // Store the current favorite icon height.
166 favoriteIconHeight = icon.height
168 // Scale the favorite icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation.
169 favoriteIcon = if (icon.height > 256 || icon.width > 256) {
170 Bitmap.createScaledBitmap(icon, 256, 256, true)
172 // Store the icon as presented.
177 fun getFavoriteIcon(): Bitmap {
178 // Return the favorite icon. This is the only way to return a non-nullable variable while retaining the custom initialization and setter functions above.
182 fun getFavoriteIconHeight(): Int {
183 // Return the favorite icon height.
184 return favoriteIconHeight
187 // Reset the handlers.
188 fun resetSslErrorHandler() {
189 // Reset the current SSL error handler.
190 sslErrorHandler = null
193 fun resetHttpAuthHandler() {
194 // Reset the current HTTP authentication handler.
195 httpAuthHandler = null
199 // Pinned SSL certificates.
200 fun hasPinnedSslCertificate(): Boolean {
201 // Return the status of the pinned SSL certificate.
202 return hasPinnedSslCertificate
205 fun setPinnedSslCertificate(issuedToCName: String, issuedToOName: String, issuedToUName: String, issuedByCName: String, issuedByOName: String, issuedByUName: String, startDate: Date, endDate: Date) {
206 // Store the pinned SSL certificate information.
207 pinnedSslIssuedToCName = issuedToCName
208 pinnedSslIssuedToOName = issuedToOName
209 pinnedSslIssuedToUName = issuedToUName
210 pinnedSslIssuedByCName = issuedByCName
211 pinnedSslIssuedByOName = issuedByOName
212 pinnedSslIssuedByUName = issuedByUName
213 pinnedSslStartDate = startDate
214 pinnedSslEndDate = endDate
216 // Set the pinned SSL certificate tracker.
217 hasPinnedSslCertificate = true
220 fun getPinnedSslCertificate(): Pair<Array<String>, Array<Date>> {
221 // Create the SSL certificate string array.
222 val sslCertificateStringArray = arrayOf(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName)
224 // Create the SSL certificate date array.
225 val sslCertificateDateArray = arrayOf(pinnedSslStartDate, pinnedSslEndDate)
227 // Return the pinned SSL certificate pair.
228 return Pair(sslCertificateStringArray, sslCertificateDateArray)
231 fun clearPinnedSslCertificate() {
232 // Clear the pinned SSL certificate.
233 pinnedSslIssuedToCName = ""
234 pinnedSslIssuedToOName = ""
235 pinnedSslIssuedToUName = ""
236 pinnedSslIssuedByCName = ""
237 pinnedSslIssuedByOName = ""
238 pinnedSslIssuedByUName = ""
239 pinnedSslStartDate = Date(0)
240 pinnedSslEndDate = Date(0)
242 // Clear the pinned SSL certificate tracker.
243 hasPinnedSslCertificate = false
247 // Resource requests.
248 fun addResourceRequest(resourceRequest: Array<String>) {
249 // Add the resource request to the list.
250 resourceRequests.add(resourceRequest)
253 fun getResourceRequests(): List<Array<String>> {
254 // Return the list of resource requests as an array list.
255 return resourceRequests
258 fun clearResourceRequests() {
259 // Clear the resource requests.
260 resourceRequests.clear()
264 // Resource request counters.
265 fun incrementRequestsCount(filterList: Int) {
266 // Increment the count of the indicated filter list.
268 BLOCKED_REQUESTS -> blockedRequests++
269 EASYLIST -> easyListBlockedRequests++
270 EASYPRIVACY -> easyPrivacyBlockedRequests++
271 FANBOYS_ANNOYANCE_LIST -> fanboysAnnoyanceListBlockedRequests++
272 FANBOYS_SOCIAL_BLOCKING_LIST -> fanboysSocialBlockingListBlockedRequests++
273 ULTRALIST -> ultraListBlockedRequests++
274 ULTRAPRIVACY -> ultraPrivacyBlockedRequests++
275 THIRD_PARTY_REQUESTS -> thirdPartyBlockedRequests++
279 fun getRequestsCount(filterList: Int): Int {
280 // Return the count of the indicated filter list.
281 return when (filterList) {
282 BLOCKED_REQUESTS -> blockedRequests
283 EASYLIST -> easyListBlockedRequests
284 EASYPRIVACY -> easyPrivacyBlockedRequests
285 FANBOYS_ANNOYANCE_LIST -> fanboysAnnoyanceListBlockedRequests
286 FANBOYS_SOCIAL_BLOCKING_LIST -> fanboysSocialBlockingListBlockedRequests
287 ULTRALIST -> ultraListBlockedRequests
288 ULTRAPRIVACY -> ultraPrivacyBlockedRequests
289 THIRD_PARTY_REQUESTS -> thirdPartyBlockedRequests
290 else -> 0 // Return 0. This should never be called, but it is required by the return when statement.
294 fun resetRequestsCounters() {
295 // Reset all the resource request counters.
297 easyListBlockedRequests = 0
298 easyPrivacyBlockedRequests = 0
299 fanboysAnnoyanceListBlockedRequests = 0
300 fanboysSocialBlockingListBlockedRequests = 0
301 ultraListBlockedRequests = 0
302 ultraPrivacyBlockedRequests = 0
303 thirdPartyBlockedRequests = 0
307 // Publicly expose the scroll ranges.
308 fun getHorizontalScrollRange(): Int {
309 // Return the horizontal scroll range.
310 return computeHorizontalScrollRange()
313 fun getVerticalScrollRange(): Int {
314 // Return the vertical scroll range.
315 return computeVerticalScrollRange()
318 override fun onOverScrolled(scrollX: Int, scrollY: Int, clampedX: Boolean, clampedY: Boolean) {
319 // Run the default commands.
320 super.onOverScrolled(scrollX, scrollY, clampedX, clampedY)
322 // Display the bottom app bar if it has been hidden and the WebView was over-scrolled at the top of the screen.
323 if ((MainWebViewActivity.appBarLayout.translationY != 0f) && (scrollY == 0) && clampedY) {
324 // Animate the bottom app bar onto the screen.
325 val objectAnimator = ObjectAnimator.ofFloat(MainWebViewActivity.appBarLayout, "translationY", 0f)
328 objectAnimator.start()
333 @SuppressLint("ClickableViewAccessibility")
334 override fun onTouchEvent(motionEvent: MotionEvent): Boolean {
335 // Run the commands for the given motion event action.
336 when (motionEvent.action) {
337 MotionEvent.ACTION_DOWN -> {
338 // Start nested scrolling along the vertical axis. `ViewCompat` must be used until the minimum API >= 21.
339 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
341 // Save the current Y position. Action down will not be called again until a new motion starts.
342 previousYPosition = motionEvent.y.toInt()
344 MotionEvent.ACTION_MOVE -> {
345 // Get the current Y position.
346 val currentYMotionPosition = motionEvent.y.toInt()
348 // Calculate the pre-scroll delta Y.
349 val preScrollDeltaY = previousYPosition - currentYMotionPosition
351 // Initialize a variable to track how much of the scroll is consumed.
352 val consumedScroll = IntArray(2)
354 // Initialize a variable to track the offset in the window.
355 val offsetInWindow = IntArray(2)
357 // Get the WebView Y position.
358 val webViewYPosition = scrollY
360 // Set the scroll delta Y to initially be the same as the pre-scroll delta Y.
361 var scrollDeltaY = preScrollDeltaY
363 // Dispatch the nested pre-school. This scrolls the app bar if it needs it. `offsetInWindow` will be returned with an updated value.
364 if (dispatchNestedPreScroll(0, preScrollDeltaY, consumedScroll, offsetInWindow)) {
365 // Update the scroll delta Y if some of it was consumed.
366 scrollDeltaY = preScrollDeltaY - consumedScroll[1]
369 // Check to see if the WebView is at the top and and the scroll action is downward.
370 if (webViewYPosition == 0 && scrollDeltaY < 0) { // Swipe to refresh is being engaged.
371 // Stop the nested scroll so that swipe to refresh has complete control. This way releasing the scroll to refresh circle doesn't scroll the WebView at the same time.
373 } else { // Swipe to refresh is not being engaged.
374 // Start the nested scroll so that the app bar can scroll off the screen.
375 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
377 // Dispatch the nested scroll. This scrolls the WebView. The delta Y unconsumed normally controls the swipe refresh layout, but that is handled with the `if` statement above.
378 dispatchNestedScroll(0, scrollDeltaY, 0, 0, offsetInWindow)
380 // Store the current Y position for use in the next action move.
381 previousYPosition -= scrollDeltaY
384 else -> stopNestedScroll() // Stop nested scrolling.
387 // Perform a click. This is required by the Android accessibility guidelines.
390 // Run the default commands and return the result.
391 return super.onTouchEvent(motionEvent)
396 fun saveNestedScrollWebViewState(): Bundle {
397 // Create a saved state bundle.
398 val savedState = Bundle()
400 // Populate the saved state bundle.
401 savedState.putBoolean(ACCEPT_COOKIES, acceptCookies)
402 savedState.putBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests)
403 savedState.putString(CURRENT_DOMAIN_NAME, currentDomainName)
404 savedState.putString(CURRENT_URL, currentUrl)
405 savedState.putBoolean(DOM_STORAGE_ENABLED, this.settings.domStorageEnabled)
406 savedState.putBoolean(DOMAIN_SETTINGS_APPLIED, domainSettingsApplied)
407 savedState.putInt(DOMAIN_SETTINGS_DATABASE_ID, domainSettingsDatabaseId)
408 savedState.putBoolean(EASYLIST_ENABLED, easyListEnabled)
409 savedState.putBoolean(EASYPRIVACY_ENABLED, easyPrivacyEnabled)
410 savedState.putBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED, fanboysAnnoyanceListEnabled)
411 savedState.putBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED, fanboysSocialBlockingListEnabled)
412 savedState.putInt(FONT_SIZE, this.settings.textZoom)
413 savedState.putBoolean(HAS_PINNED_SSL_CERTIFICATE, hasPinnedSslCertificate)
414 savedState.putBoolean(IGNORE_PINNED_DOMAIN_INFORMATION, ignorePinnedDomainInformation)
415 savedState.putBoolean(JAVASCRIPT_ENABLED, this.settings.javaScriptEnabled)
416 savedState.putString(PINNED_IP_ADDRESSES, pinnedIpAddresses)
417 savedState.putLong(PINNED_SSL_END_DATE, pinnedSslEndDate.time)
418 savedState.putString(PINNED_SSL_ISSUED_BY_CNAME, pinnedSslIssuedByCName)
419 savedState.putString(PINNED_SSL_ISSUED_BY_ONAME, pinnedSslIssuedByOName)
420 savedState.putString(PINNED_SSL_ISSUED_BY_UNAME, pinnedSslIssuedByUName)
421 savedState.putString(PINNED_SSL_ISSUED_TO_CNAME, pinnedSslIssuedToCName)
422 savedState.putString(PINNED_SSL_ISSUED_TO_ONAME, pinnedSslIssuedToOName)
423 savedState.putString(PINNED_SSL_ISSUED_TO_UNAME, pinnedSslIssuedToUName)
424 savedState.putLong(PINNED_SSL_START_DATE, pinnedSslStartDate.time)
425 savedState.putBoolean(SWIPE_TO_REFRESH, swipeToRefresh)
426 savedState.putBoolean(ULTRALIST_ENABLED, ultraListEnabled)
427 savedState.putBoolean(ULTRAPRIVACY_ENABLED, ultraPrivacyEnabled)
428 savedState.putString(USER_AGENT, this.settings.userAgentString)
429 savedState.putBoolean(WIDE_VIEWPORT, this.settings.useWideViewPort)
431 // Return the saved state bundle.
435 // Restore the state.
436 fun restoreNestedScrollWebViewState(savedState: Bundle) {
437 // Restore the class variables.
438 acceptCookies = savedState.getBoolean(ACCEPT_COOKIES)
439 blockAllThirdPartyRequests = savedState.getBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS)
440 currentDomainName = savedState.getString(CURRENT_DOMAIN_NAME)!!
441 currentUrl = savedState.getString(CURRENT_URL)!!
442 this.settings.domStorageEnabled = savedState.getBoolean(DOM_STORAGE_ENABLED)
443 domainSettingsApplied = savedState.getBoolean(DOMAIN_SETTINGS_APPLIED)
444 domainSettingsDatabaseId = savedState.getInt(DOMAIN_SETTINGS_DATABASE_ID)
445 easyListEnabled = savedState.getBoolean(EASYLIST_ENABLED)
446 easyPrivacyEnabled = savedState.getBoolean(EASYPRIVACY_ENABLED)
447 fanboysAnnoyanceListEnabled = savedState.getBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED)
448 fanboysSocialBlockingListEnabled = savedState.getBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED)
449 this.settings.textZoom = savedState.getInt(FONT_SIZE)
450 hasPinnedSslCertificate = savedState.getBoolean(HAS_PINNED_SSL_CERTIFICATE)
451 ignorePinnedDomainInformation = savedState.getBoolean(IGNORE_PINNED_DOMAIN_INFORMATION)
452 this.settings.javaScriptEnabled = savedState.getBoolean(JAVASCRIPT_ENABLED)
453 pinnedIpAddresses = savedState.getString(PINNED_IP_ADDRESSES)!!
454 pinnedSslEndDate = Date(savedState.getLong(PINNED_SSL_END_DATE))
455 pinnedSslIssuedByCName = savedState.getString(PINNED_SSL_ISSUED_BY_CNAME)!!
456 pinnedSslIssuedByOName = savedState.getString(PINNED_SSL_ISSUED_BY_ONAME)!!
457 pinnedSslIssuedByUName = savedState.getString(PINNED_SSL_ISSUED_BY_UNAME)!!
458 pinnedSslIssuedToCName = savedState.getString(PINNED_SSL_ISSUED_TO_CNAME)!!
459 pinnedSslIssuedToOName = savedState.getString(PINNED_SSL_ISSUED_TO_ONAME)!!
460 pinnedSslIssuedToUName = savedState.getString(PINNED_SSL_ISSUED_TO_UNAME)!!
461 pinnedSslStartDate = Date(savedState.getLong(PINNED_SSL_START_DATE))
462 swipeToRefresh = savedState.getBoolean(SWIPE_TO_REFRESH)
463 ultraListEnabled = savedState.getBoolean(ULTRALIST_ENABLED)
464 ultraPrivacyEnabled = savedState.getBoolean(ULTRAPRIVACY_ENABLED)
465 this.settings.userAgentString = savedState.getString(USER_AGENT)
466 this.settings.useWideViewPort = savedState.getBoolean(WIDE_VIEWPORT)
470 // Method from NestedScrollingChild.
471 override fun setNestedScrollingEnabled(status: Boolean) {
472 // Set the status of the nested scrolling.
473 nestedScrollingChildHelper.isNestedScrollingEnabled = status
476 // Method from NestedScrollingChild.
477 override fun isNestedScrollingEnabled(): Boolean {
478 // Return the status of nested scrolling.
479 return nestedScrollingChildHelper.isNestedScrollingEnabled
482 // Method from NestedScrollingChild.
483 override fun startNestedScroll(axes: Int): Boolean {
484 // Start a nested scroll along the indicated axes.
485 return nestedScrollingChildHelper.startNestedScroll(axes)
488 // Method from NestedScrollingChild2.
489 override fun startNestedScroll(axes: Int, type: Int): Boolean {
490 // Start a nested scroll along the indicated axes for the given type of input which caused the scroll event.
491 return nestedScrollingChildHelper.startNestedScroll(axes, type)
494 // Method from NestedScrollingChild.
495 override fun stopNestedScroll() {
496 // Stop the nested scroll.
497 nestedScrollingChildHelper.stopNestedScroll()
500 // Method from NestedScrollingChild2.
501 override fun stopNestedScroll(type: Int) {
502 // Stop the nested scroll of the given type of input which caused the scroll event.
503 nestedScrollingChildHelper.stopNestedScroll(type)
506 // Method from NestedScrollingChild.
507 override fun hasNestedScrollingParent(): Boolean {
508 // Return the status of the nested scrolling parent.
509 return nestedScrollingChildHelper.hasNestedScrollingParent()
512 // Method from NestedScrollingChild2.
513 override fun hasNestedScrollingParent(type: Int): Boolean {
514 // return the status of the nested scrolling parent for the given type of input which caused the scroll event.
515 return nestedScrollingChildHelper.hasNestedScrollingParent(type)
518 // Method from NestedScrollingChild.
519 override fun dispatchNestedPreScroll(deltaX: Int, deltaY: Int, consumed: IntArray?, offsetInWindow: IntArray?): Boolean {
520 // Dispatch a nested pre-scroll with the specified deltas, which lets a parent to consume some of the scroll if desired.
521 return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow)
524 // Method from NestedScrollingChild2.
525 override fun dispatchNestedPreScroll(deltaX: Int, deltaY: Int, consumed: IntArray?, offsetInWindow: IntArray?, type: Int): Boolean {
526 // Dispatch a nested pre-scroll with the specified deltas for the given type of input which caused the scroll event, which lets a parent to consume some of the scroll if desired.
527 return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow, type)
530 // Method from NestedScrollingChild.
531 override fun dispatchNestedScroll(deltaXConsumed: Int, deltaYConsumed: Int, deltaXUnconsumed: Int, deltaYUnconsumed: Int, offsetInWindow: IntArray?): Boolean {
532 // Dispatch a nested scroll with the specified deltas.
533 return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow)
536 // Method from NestedScrollingChild2.
537 override fun dispatchNestedScroll(deltaXConsumed: Int, deltaYConsumed: Int, deltaXUnconsumed: Int, deltaYUnconsumed: Int, offsetInWindow: IntArray?, type: Int): Boolean {
538 // Dispatch a nested scroll with the specified deltas for the given type of input which caused the scroll event.
539 return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow, type)
542 // Method from NestedScrollingChild.
543 override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float): Boolean {
544 // Dispatch a nested pre-fling with the specified velocity, which lets a parent consume the fling if desired.
545 return nestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY)
548 // Method from NestedScrollingChild.
549 override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean): Boolean {
550 // Dispatch a nested fling with the specified velocity.
551 return nestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed)