2 * Copyright © 2019-2021 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
6 * Privacy Browser 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 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. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacybrowser.views
22 import android.annotation.SuppressLint
23 import android.content.Context
24 import android.graphics.Bitmap
25 import android.graphics.drawable.BitmapDrawable
26 import android.os.Bundle
27 import android.util.AttributeSet
28 import android.view.MotionEvent
29 import android.webkit.WebView
30 import android.webkit.SslErrorHandler
31 import android.webkit.HttpAuthHandler
33 import androidx.core.content.ContextCompat
34 import androidx.core.view.NestedScrollingChild2
35 import androidx.core.view.NestedScrollingChildHelper
36 import androidx.core.view.ViewCompat
38 import com.stoutner.privacybrowser.R
40 import java.util.Collections
43 import kotlin.collections.ArrayList
45 import kotlin.jvm.JvmOverloads
47 // Define the saved state constants.
48 private const val DOMAIN_SETTINGS_APPLIED = "domain_settings_applied"
49 private const val DOMAIN_SETTINGS_DATABASE_ID = "domain_settings_database_id"
50 private const val CURRENT_DOMAIN_NAME = "current_domain_name"
51 private const val CURRENT_URl = "current_url"
52 private const val ACCEPT_COOKIES = "accept_cookies"
53 private const val EASYLIST_ENABLED = "easylist_enabled"
54 private const val EASYPRIVACY_ENABLED = "easyprivacy_enabled"
55 private const val FANBOYS_ANNOYANCE_LIST_ENABLED = "fanboys_annoyance_list_enabled"
56 private const val FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED = "fanboys_social_blocking_list_enabled"
57 private const val ULTRALIST_ENABLED = "ultralist_enabled"
58 private const val ULTRAPRIVACY_ENABLED = "ultraprivacy_enabled"
59 private const val BLOCK_ALL_THIRD_PARTY_REQUESTS = "block_all_third_party_requests"
60 private const val HAS_PINNED_SSL_CERTIFICATE = "has_pinned_ssl_certificate"
61 private const val PINNED_SSL_ISSUED_TO_CNAME = "pinned_ssl_issued_to_cname"
62 private const val PINNED_SSL_ISSUED_TO_ONAME = "pinned_ssl_issued_to_oname"
63 private const val PINNED_SSL_ISSUED_TO_UNAME = "pinned_ssl_issued_to_uname"
64 private const val PINNED_SSL_ISSUED_BY_CNAME = "pinned_ssl_issued_by_cname"
65 private const val PINNED_SSL_ISSUED_BY_ONAME = "pinned_ssl_issued_by_oname"
66 private const val PINNED_SSL_ISSUED_BY_UNAME = "pinned_ssl_issued_by_uname"
67 private const val PINNED_SSL_START_DATE = "pinned_ssl_start_date"
68 private const val PINNED_SSL_END_DATE = "pinned_ssl_end_date"
69 private const val PINNED_IP_ADDRESSES = "pinned_ip_addresses"
70 private const val IGNORE_PINNED_DOMAIN_INFORMATION = "ignore_pinned_domain_information"
71 private const val SWIPE_TO_REFRESH = "swipe_to_refresh"
72 private const val JAVASCRIPT_ENABLED = "javascript_enabled"
73 private const val DOM_STORAGE_ENABLED = "dom_storage_enabled"
74 private const val USER_AGENT = "user_agent"
75 private const val WIDE_VIEWPORT = "wide_viewport"
76 private const val FONT_SIZE = "font_size"
78 // 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.
79 class NestedScrollWebView @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null, defaultStyle: Int = android.R.attr.webViewStyle) : WebView(context, attributeSet, defaultStyle),
80 NestedScrollingChild2 {
83 // Define the companion object blocklists constants. These can be moved to class constants once all of the code has transitioned to Kotlin.
84 const val BLOCKED_REQUESTS = 0
85 const val EASYLIST = 1
86 const val EASYPRIVACY = 2
87 const val FANBOYS_ANNOYANCE_LIST = 3
88 const val FANBOYS_SOCIAL_BLOCKING_LIST = 4
89 const val ULTRALIST = 5
90 const val ULTRAPRIVACY = 6
91 const val THIRD_PARTY_REQUESTS = 7
94 // Define the public variables.
95 var acceptCookies = false
96 var blockAllThirdPartyRequests = false
97 var currentDomainName = ""
98 var currentIpAddresses = ""
100 var domainSettingsApplied = false
101 var domainSettingsDatabaseId = 0
102 var easyListEnabled = true
103 var easyPrivacyEnabled = true
104 var fanboysAnnoyanceListEnabled = true
105 var fanboysSocialBlockingListEnabled = true
106 var httpAuthHandler: HttpAuthHandler? = null
107 var ignorePinnedDomainInformation = false
108 var pinnedIpAddresses = ""
109 var sslErrorHandler: SslErrorHandler? = null
110 var swipeToRefresh = false
111 var ultraListEnabled = true
112 var ultraPrivacyEnabled = true
113 var waitingForProxyUrlString = ""
114 var webViewFragmentId: Long = 0
117 // Define the private variables.
118 private val nestedScrollingChildHelper: NestedScrollingChildHelper = NestedScrollingChildHelper(this)
119 private lateinit var favoriteOrDefaultIcon: Bitmap
120 private var previousYPosition = 0 // The previous Y position needs to be tracked between motion events.
121 private var hasPinnedSslCertificate = false
122 private var pinnedSslIssuedToCName = ""
123 private var pinnedSslIssuedToOName = ""
124 private var pinnedSslIssuedToUName = ""
125 private var pinnedSslIssuedByCName = ""
126 private var pinnedSslIssuedByOName = ""
127 private var pinnedSslIssuedByUName = ""
128 private var pinnedSslStartDate = Date(0)
129 private var pinnedSslEndDate = Date(0)
130 private val resourceRequests = Collections.synchronizedList(ArrayList<Array<String>>()) // Using a synchronized list makes adding resource requests thread safe.
131 private var blockedRequests = 0
132 private var easyListBlockedRequests = 0
133 private var easyPrivacyBlockedRequests = 0
134 private var fanboysAnnoyanceListBlockedRequests = 0
135 private var fanboysSocialBlockingListBlockedRequests = 0
136 private var ultraListBlockedRequests = 0
137 private var ultraPrivacyBlockedRequests = 0
138 private var thirdPartyBlockedRequests = 0
141 // Enable nested scrolling by default.
142 nestedScrollingChildHelper.isNestedScrollingEnabled = true
144 // Initialize the favorite icon.
145 initializeFavoriteIcon()
149 // Favorite or default icon.
150 fun initializeFavoriteIcon() {
151 // Get the default favorite icon drawable. `ContextCompat` must be used until API >= 21.
152 val favoriteIconDrawable = ContextCompat.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 favoriteOrDefaultIcon = favoriteIconBitmapDrawable.bitmap
161 fun setFavoriteOrDefaultIcon(icon: Bitmap) {
162 // Scale the favorite icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation.
163 favoriteOrDefaultIcon = if (icon.height > 256 || icon.width > 256) {
164 Bitmap.createScaledBitmap(icon, 256, 256, true)
166 // Store the icon as presented.
171 fun getFavoriteOrDefaultIcon(): Bitmap {
172 // Return the favorite or default icon. This is the only way to return a non-nullable variable while retaining the custom initialization and setter functions above.
173 return favoriteOrDefaultIcon
177 // Reset the handlers.
178 fun resetSslErrorHandler() {
179 // Reset the current SSL error handler.
180 sslErrorHandler = null
183 fun resetHttpAuthHandler() {
184 // Reset the current HTTP authentication handler.
185 httpAuthHandler = null
189 // Pinned SSL certificates.
190 fun hasPinnedSslCertificate(): Boolean {
191 // Return the status of the pinned SSL certificate.
192 return hasPinnedSslCertificate
195 fun setPinnedSslCertificate(issuedToCName: String, issuedToOName: String, issuedToUName: String, issuedByCName: String, issuedByOName: String, issuedByUName: String, startDate: Date, endDate: Date) {
196 // Store the pinned SSL certificate information.
197 pinnedSslIssuedToCName = issuedToCName
198 pinnedSslIssuedToOName = issuedToOName
199 pinnedSslIssuedToUName = issuedToUName
200 pinnedSslIssuedByCName = issuedByCName
201 pinnedSslIssuedByOName = issuedByOName
202 pinnedSslIssuedByUName = issuedByUName
203 pinnedSslStartDate = startDate
204 pinnedSslEndDate = endDate
206 // Set the pinned SSL certificate tracker.
207 hasPinnedSslCertificate = true
210 fun getPinnedSslCertificate(): ArrayList<Any> {
211 // Initialize an array list.
212 val arrayList = ArrayList<Any>()
214 // Create the SSL certificate string array.
215 val sslCertificateStringArray = arrayOf(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName)
217 // Create the SSL certificate date array.
218 val sslCertificateDateArray = arrayOf(pinnedSslStartDate, pinnedSslEndDate)
220 // Add the arrays to the array list.
221 arrayList.add(sslCertificateStringArray)
222 arrayList.add(sslCertificateDateArray)
224 // Return the pinned SSL certificate array list.
228 fun clearPinnedSslCertificate() {
229 // Clear the pinned SSL certificate.
230 pinnedSslIssuedToCName = ""
231 pinnedSslIssuedToOName = ""
232 pinnedSslIssuedToUName = ""
233 pinnedSslIssuedByCName = ""
234 pinnedSslIssuedByOName = ""
235 pinnedSslIssuedByUName = ""
236 pinnedSslStartDate = Date(0)
237 pinnedSslEndDate = Date(0)
239 // Clear the pinned SSL certificate tracker.
240 hasPinnedSslCertificate = false
244 // Resource requests.
245 fun addResourceRequest(resourceRequest: Array<String>) {
246 // Add the resource request to the list.
247 resourceRequests.add(resourceRequest)
250 fun getResourceRequests(): List<Array<String>> {
251 // Return the list of resource requests as an array list.
252 return resourceRequests
255 fun clearResourceRequests() {
256 // Clear the resource requests.
257 resourceRequests.clear()
261 // Resource request counters.
262 fun incrementRequestsCount(blocklist: Int) {
263 // Increment the count of the indicated blocklist.
265 BLOCKED_REQUESTS -> blockedRequests++
266 EASYLIST -> easyListBlockedRequests++
267 EASYPRIVACY -> easyPrivacyBlockedRequests++
268 FANBOYS_ANNOYANCE_LIST -> fanboysAnnoyanceListBlockedRequests++
269 FANBOYS_SOCIAL_BLOCKING_LIST -> fanboysSocialBlockingListBlockedRequests++
270 ULTRALIST -> ultraListBlockedRequests++
271 ULTRAPRIVACY -> ultraPrivacyBlockedRequests++
272 THIRD_PARTY_REQUESTS -> thirdPartyBlockedRequests++
276 fun getRequestsCount(blocklist: Int): Int {
277 // Return the count of the indicated blocklist.
278 return when (blocklist) {
279 BLOCKED_REQUESTS -> blockedRequests
280 EASYLIST -> easyListBlockedRequests
281 EASYPRIVACY -> easyPrivacyBlockedRequests
282 FANBOYS_ANNOYANCE_LIST -> fanboysAnnoyanceListBlockedRequests
283 FANBOYS_SOCIAL_BLOCKING_LIST -> fanboysSocialBlockingListBlockedRequests
284 ULTRALIST -> ultraListBlockedRequests
285 ULTRAPRIVACY -> ultraPrivacyBlockedRequests
286 THIRD_PARTY_REQUESTS -> thirdPartyBlockedRequests
287 else -> 0 // Return 0. This should never be called, but it is required by the return when statement.
291 fun resetRequestsCounters() {
292 // Reset all the resource request counters.
294 easyListBlockedRequests = 0
295 easyPrivacyBlockedRequests = 0
296 fanboysAnnoyanceListBlockedRequests = 0
297 fanboysSocialBlockingListBlockedRequests = 0
298 ultraListBlockedRequests = 0
299 ultraPrivacyBlockedRequests = 0
300 thirdPartyBlockedRequests = 0
304 // Publicly expose the scroll ranges.
305 fun getHorizontalScrollRange(): Int {
306 // Return the horizontal scroll range.
307 return computeHorizontalScrollRange()
310 fun getVerticalScrollRange(): Int {
311 // Return the vertical scroll range.
312 return computeVerticalScrollRange()
317 @SuppressLint("ClickableViewAccessibility")
318 override fun onTouchEvent(motionEvent: MotionEvent): Boolean {
319 // Run the commands for the given motion event action.
320 when (motionEvent.action) {
321 MotionEvent.ACTION_DOWN -> {
322 // Start nested scrolling along the vertical axis. `ViewCompat` must be used until the minimum API >= 21.
323 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
325 // Save the current Y position. Action down will not be called again until a new motion starts.
326 previousYPosition = motionEvent.y.toInt()
328 MotionEvent.ACTION_MOVE -> {
329 // Get the current Y position.
330 val currentYMotionPosition = motionEvent.y.toInt()
332 // Calculate the pre-scroll delta Y.
333 val preScrollDeltaY = previousYPosition - currentYMotionPosition
335 // Initialize a variable to track how much of the scroll is consumed.
336 val consumedScroll = IntArray(2)
338 // Initialize a variable to track the offset in the window.
339 val offsetInWindow = IntArray(2)
341 // Get the WebView Y position.
342 val webViewYPosition = scrollY
344 // Set the scroll delta Y to initially be the same as the pre-scroll delta Y.
345 var scrollDeltaY = preScrollDeltaY
347 // Dispatch the nested pre-school. This scrolls the app bar if it needs it. `offsetInWindow` will be returned with an updated value.
348 if (dispatchNestedPreScroll(0, preScrollDeltaY, consumedScroll, offsetInWindow)) {
349 // Update the scroll delta Y if some of it was consumed.
350 scrollDeltaY = preScrollDeltaY - consumedScroll[1]
353 // Check to see if the WebView is at the top and and the scroll action is downward.
354 if (webViewYPosition == 0 && scrollDeltaY < 0) { // Swipe to refresh is being engaged.
355 // 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.
357 } else { // Swipe to refresh is not being engaged.
358 // Start the nested scroll so that the app bar can scroll off the screen.
359 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
361 // 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.
362 dispatchNestedScroll(0, scrollDeltaY, 0, 0, offsetInWindow)
364 // Store the current Y position for use in the next action move.
365 previousYPosition -= scrollDeltaY
368 else -> stopNestedScroll() // Stop nested scrolling.
371 // Perform a click. This is required by the Android accessibility guidelines.
374 // Run the default commands and return the result.
375 return super.onTouchEvent(motionEvent)
379 // Save and restore state.
380 fun saveNestedScrollWebViewState(): Bundle {
381 // Create a saved state bundle.
382 val savedState = Bundle()
384 // Populate the saved state bundle.
385 savedState.putBoolean(DOMAIN_SETTINGS_APPLIED, domainSettingsApplied)
386 savedState.putInt(DOMAIN_SETTINGS_DATABASE_ID, domainSettingsDatabaseId)
387 savedState.putString(CURRENT_DOMAIN_NAME, currentDomainName)
388 savedState.putString(CURRENT_URl, currentUrl)
389 savedState.putBoolean(ACCEPT_COOKIES, acceptCookies)
390 savedState.putBoolean(EASYLIST_ENABLED, easyListEnabled)
391 savedState.putBoolean(EASYPRIVACY_ENABLED, easyPrivacyEnabled)
392 savedState.putBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED, fanboysAnnoyanceListEnabled)
393 savedState.putBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED, fanboysSocialBlockingListEnabled)
394 savedState.putBoolean(ULTRALIST_ENABLED, ultraListEnabled)
395 savedState.putBoolean(ULTRAPRIVACY_ENABLED, ultraPrivacyEnabled)
396 savedState.putBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests)
397 savedState.putBoolean(HAS_PINNED_SSL_CERTIFICATE, hasPinnedSslCertificate)
398 savedState.putString(PINNED_SSL_ISSUED_TO_CNAME, pinnedSslIssuedToCName)
399 savedState.putString(PINNED_SSL_ISSUED_TO_ONAME, pinnedSslIssuedToOName)
400 savedState.putString(PINNED_SSL_ISSUED_TO_UNAME, pinnedSslIssuedToUName)
401 savedState.putString(PINNED_SSL_ISSUED_BY_CNAME, pinnedSslIssuedByCName)
402 savedState.putString(PINNED_SSL_ISSUED_BY_ONAME, pinnedSslIssuedByOName)
403 savedState.putString(PINNED_SSL_ISSUED_BY_UNAME, pinnedSslIssuedByUName)
404 savedState.putLong(PINNED_SSL_START_DATE, pinnedSslStartDate.time)
405 savedState.putLong(PINNED_SSL_END_DATE, pinnedSslEndDate.time)
406 savedState.putString(PINNED_IP_ADDRESSES, pinnedIpAddresses)
407 savedState.putBoolean(IGNORE_PINNED_DOMAIN_INFORMATION, ignorePinnedDomainInformation)
408 savedState.putBoolean(SWIPE_TO_REFRESH, swipeToRefresh)
409 savedState.putBoolean(JAVASCRIPT_ENABLED, this.settings.javaScriptEnabled)
410 savedState.putBoolean(DOM_STORAGE_ENABLED, this.settings.domStorageEnabled)
411 savedState.putString(USER_AGENT, this.settings.userAgentString)
412 savedState.putBoolean(WIDE_VIEWPORT, this.settings.useWideViewPort)
413 savedState.putInt(FONT_SIZE, this.settings.textZoom)
415 // Return the saved state bundle.
419 fun restoreNestedScrollWebViewState(savedState: Bundle) {
420 // Restore the class variables.
421 domainSettingsApplied = savedState.getBoolean(DOMAIN_SETTINGS_APPLIED)
422 domainSettingsDatabaseId = savedState.getInt(DOMAIN_SETTINGS_DATABASE_ID)
423 currentDomainName = savedState.getString(CURRENT_DOMAIN_NAME)!!
424 currentUrl = savedState.getString(CURRENT_URl)!!
425 acceptCookies = savedState.getBoolean(ACCEPT_COOKIES)
426 easyListEnabled = savedState.getBoolean(EASYLIST_ENABLED)
427 easyPrivacyEnabled = savedState.getBoolean(EASYPRIVACY_ENABLED)
428 fanboysAnnoyanceListEnabled = savedState.getBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED)
429 fanboysSocialBlockingListEnabled = savedState.getBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED)
430 ultraListEnabled = savedState.getBoolean(ULTRALIST_ENABLED)
431 ultraPrivacyEnabled = savedState.getBoolean(ULTRAPRIVACY_ENABLED)
432 blockAllThirdPartyRequests = savedState.getBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS)
433 hasPinnedSslCertificate = savedState.getBoolean(HAS_PINNED_SSL_CERTIFICATE)
434 pinnedSslIssuedToCName = savedState.getString(PINNED_SSL_ISSUED_TO_CNAME)!!
435 pinnedSslIssuedToOName = savedState.getString(PINNED_SSL_ISSUED_TO_ONAME)!!
436 pinnedSslIssuedToUName = savedState.getString(PINNED_SSL_ISSUED_TO_UNAME)!!
437 pinnedSslIssuedByCName = savedState.getString(PINNED_SSL_ISSUED_BY_CNAME)!!
438 pinnedSslIssuedByOName = savedState.getString(PINNED_SSL_ISSUED_BY_ONAME)!!
439 pinnedSslIssuedByUName = savedState.getString(PINNED_SSL_ISSUED_BY_UNAME)!!
440 pinnedSslStartDate = Date(savedState.getLong(PINNED_SSL_START_DATE))
441 pinnedSslEndDate = Date(savedState.getLong(PINNED_SSL_END_DATE))
442 pinnedIpAddresses = savedState.getString(PINNED_IP_ADDRESSES)!!
443 ignorePinnedDomainInformation = savedState.getBoolean(IGNORE_PINNED_DOMAIN_INFORMATION)
444 swipeToRefresh = savedState.getBoolean(SWIPE_TO_REFRESH)
445 this.settings.javaScriptEnabled = savedState.getBoolean(JAVASCRIPT_ENABLED)
446 this.settings.domStorageEnabled = savedState.getBoolean(DOM_STORAGE_ENABLED)
447 this.settings.userAgentString = savedState.getString(USER_AGENT)
448 this.settings.useWideViewPort = savedState.getBoolean(WIDE_VIEWPORT)
449 this.settings.textZoom = savedState.getInt(FONT_SIZE)
453 // Method from NestedScrollingChild.
454 override fun setNestedScrollingEnabled(status: Boolean) {
455 // Set the status of the nested scrolling.
456 nestedScrollingChildHelper.isNestedScrollingEnabled = status
459 // Method from NestedScrollingChild.
460 override fun isNestedScrollingEnabled(): Boolean {
461 // Return the status of nested scrolling.
462 return nestedScrollingChildHelper.isNestedScrollingEnabled
465 // Method from NestedScrollingChild.
466 override fun startNestedScroll(axes: Int): Boolean {
467 // Start a nested scroll along the indicated axes.
468 return nestedScrollingChildHelper.startNestedScroll(axes)
471 // Method from NestedScrollingChild2.
472 override fun startNestedScroll(axes: Int, type: Int): Boolean {
473 // Start a nested scroll along the indicated axes for the given type of input which caused the scroll event.
474 return nestedScrollingChildHelper.startNestedScroll(axes, type)
477 // Method from NestedScrollingChild.
478 override fun stopNestedScroll() {
479 // Stop the nested scroll.
480 nestedScrollingChildHelper.stopNestedScroll()
483 // Method from NestedScrollingChild2.
484 override fun stopNestedScroll(type: Int) {
485 // Stop the nested scroll of the given type of input which caused the scroll event.
486 nestedScrollingChildHelper.stopNestedScroll(type)
489 // Method from NestedScrollingChild.
490 override fun hasNestedScrollingParent(): Boolean {
491 // Return the status of the nested scrolling parent.
492 return nestedScrollingChildHelper.hasNestedScrollingParent()
495 // Method from NestedScrollingChild2.
496 override fun hasNestedScrollingParent(type: Int): Boolean {
497 // return the status of the nested scrolling parent for the given type of input which caused the scroll event.
498 return nestedScrollingChildHelper.hasNestedScrollingParent(type)
501 // Method from NestedScrollingChild.
502 override fun dispatchNestedPreScroll(deltaX: Int, deltaY: Int, consumed: IntArray?, offsetInWindow: IntArray?): Boolean {
503 // Dispatch a nested pre-scroll with the specified deltas, which lets a parent to consume some of the scroll if desired.
504 return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow)
507 // Method from NestedScrollingChild2.
508 override fun dispatchNestedPreScroll(deltaX: Int, deltaY: Int, consumed: IntArray?, offsetInWindow: IntArray?, type: Int): Boolean {
509 // 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.
510 return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow, type)
513 // Method from NestedScrollingChild.
514 override fun dispatchNestedScroll(deltaXConsumed: Int, deltaYConsumed: Int, deltaXUnconsumed: Int, deltaYUnconsumed: Int, offsetInWindow: IntArray?): Boolean {
515 // Dispatch a nested scroll with the specified deltas.
516 return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow)
519 // Method from NestedScrollingChild2.
520 override fun dispatchNestedScroll(deltaXConsumed: Int, deltaYConsumed: Int, deltaXUnconsumed: Int, deltaYUnconsumed: Int, offsetInWindow: IntArray?, type: Int): Boolean {
521 // Dispatch a nested scroll with the specified deltas for the given type of input which caused the scroll event.
522 return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow, type)
525 // Method from NestedScrollingChild.
526 override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float): Boolean {
527 // Dispatch a nested pre-fling with the specified velocity, which lets a parent consume the fling if desired.
528 return nestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY)
531 // Method from NestedScrollingChild.
532 override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean): Boolean {
533 // Dispatch a nested fling with the specified velocity.
534 return nestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed)