2 * Copyright 2019-2024 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.Drawable
27 import android.os.Bundle
28 import android.util.AttributeSet
29 import android.view.MotionEvent
30 import android.webkit.HttpAuthHandler
31 import android.webkit.SslErrorHandler
32 import android.webkit.WebView
34 import androidx.core.view.NestedScrollingChild2
35 import androidx.core.view.NestedScrollingChildHelper
36 import androidx.core.view.ViewCompat
38 import com.stoutner.privacybrowser.activities.MainWebViewActivity
40 import java.util.Collections
43 import kotlin.collections.ArrayList
45 // Define the public constants.
46 const val BLOCKED_REQUESTS = 0
47 const val EASYLIST = 1
48 const val EASYPRIVACY = 2
49 const val FANBOYS_ANNOYANCE_LIST = 3
50 const val FANBOYS_SOCIAL_BLOCKING_LIST = 4
51 const val ULTRALIST = 5
52 const val ULTRAPRIVACY = 6
53 const val THIRD_PARTY_REQUESTS = 7
55 // Define the private class constants.
56 private const val ACCEPT_COOKIES = "A"
57 private const val BLOCK_ALL_THIRD_PARTY_REQUESTS = "B"
58 private const val CURRENT_DOMAIN_NAME = "C"
59 private const val CURRENT_URL = "D"
60 private const val DOM_STORAGE_ENABLED = "E"
61 private const val DOMAIN_SETTINGS_APPLIED = "F"
62 private const val DOMAIN_SETTINGS_DATABASE_ID = "G"
63 private const val EASYLIST_ENABLED = "H"
64 private const val EASYPRIVACY_ENABLED = "I"
65 private const val FANBOYS_ANNOYANCE_LIST_ENABLED = "J"
66 private const val FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED = "K"
67 private const val FONT_SIZE = "L"
68 private const val HAS_PINNED_SSL_CERTIFICATE = "M"
69 private const val IGNORE_PINNED_DOMAIN_INFORMATION = "N"
70 private const val JAVASCRIPT_ENABLED = "O"
71 private const val PINNED_IP_ADDRESSES = "P"
72 private const val PINNED_SSL_END_DATE = "Q"
73 private const val PINNED_SSL_ISSUED_BY_CNAME = "R"
74 private const val PINNED_SSL_ISSUED_BY_ONAME = "S"
75 private const val PINNED_SSL_ISSUED_BY_UNAME = "T"
76 private const val PINNED_SSL_ISSUED_TO_CNAME = "U"
77 private const val PINNED_SSL_ISSUED_TO_ONAME = "V"
78 private const val PINNED_SSL_ISSUED_TO_UNAME = "W"
79 private const val PINNED_SSL_START_DATE = "X"
80 private const val SWIPE_TO_REFRESH = "Y"
81 private const val ULTRALIST_ENABLED = "Z"
82 private const val ULTRAPRIVACY_ENABLED = "AA"
83 private const val USER_AGENT = "AB"
84 private const val WIDE_VIEWPORT = "AC"
86 // 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.
87 class NestedScrollWebView @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null, defaultStyle: Int = android.R.attr.webViewStyle) : WebView(context, attributeSet, defaultStyle),
88 NestedScrollingChild2 {
90 // Define the public variables.
91 var acceptCookies = false
92 var blockAllThirdPartyRequests = false
93 var currentDomainName = ""
94 var currentIpAddresses = ""
96 var domainSettingsApplied = false
97 var domainSettingsDatabaseId = 0
98 var easyListEnabled = true
99 var easyPrivacyEnabled = true
100 var fanboysAnnoyanceListEnabled = true
101 var fanboysSocialBlockingListEnabled = true
102 var httpAuthHandler: HttpAuthHandler? = null
103 var ignorePinnedDomainInformation = false
104 var pinnedIpAddresses = ""
105 var previousFavoriteIconDrawable: Drawable? = null
106 var previousWebpageTitle = ""
107 var sslErrorHandler: SslErrorHandler? = null
108 var swipeToRefresh = false
109 var ultraListEnabled = true
110 var ultraPrivacyEnabled = true
111 var waitingForProxyUrlString = ""
112 var webViewFragmentId: Long = 0
114 // Define the private variables.
115 private val nestedScrollingChildHelper: NestedScrollingChildHelper = NestedScrollingChildHelper(this)
116 private lateinit var favoriteIcon: Bitmap
117 private var favoriteIconHeight = 0
118 private var previousYPosition = 0 // The previous Y position needs to be tracked between motion events.
119 private var hasPinnedSslCertificate = false
120 private var pinnedSslIssuedToCName = ""
121 private var pinnedSslIssuedToOName = ""
122 private var pinnedSslIssuedToUName = ""
123 private var pinnedSslIssuedByCName = ""
124 private var pinnedSslIssuedByOName = ""
125 private var pinnedSslIssuedByUName = ""
126 private var pinnedSslStartDate = Date(0)
127 private var pinnedSslEndDate = Date(0)
128 private val resourceRequests = Collections.synchronizedList(ArrayList<Array<String>>()) // Using a synchronized list makes adding resource requests thread safe.
129 private var blockedRequests = 0
130 private var easyListBlockedRequests = 0
131 private var easyPrivacyBlockedRequests = 0
132 private var fanboysAnnoyanceListBlockedRequests = 0
133 private var fanboysSocialBlockingListBlockedRequests = 0
134 private var ultraListBlockedRequests = 0
135 private var ultraPrivacyBlockedRequests = 0
136 private var thirdPartyBlockedRequests = 0
139 // Enable nested scrolling by default.
140 nestedScrollingChildHelper.isNestedScrollingEnabled = true
142 // Initialize the favorite icon.
146 // Favorite or default icon.
147 fun resetFavoriteIcon() {
148 // Store the default icon bitmap.
149 favoriteIcon = MainWebViewActivity.defaultFavoriteIconBitmap
151 // Set the favorite icon height to be 0. This way any favorite icons presented by the website will overwrite it.
152 favoriteIconHeight = 0
155 fun setFavoriteIcon(icon: Bitmap) {
156 // Store the current favorite icon height.
157 favoriteIconHeight = icon.height
159 // Scale the favorite icon bitmap down if it is larger than 128 in either direction. Filtering uses bilinear interpolation.
160 favoriteIcon = if (icon.height > 128 || icon.width > 128) // Scale the icon before storing it.
161 Bitmap.createScaledBitmap(icon, 128, 128, true)
162 else // Store the icon as presented.
166 fun getFavoriteIcon(): Bitmap {
167 // Return the favorite icon. This is the only way to return a non-nullable variable while retaining the custom initialization and setter functions above.
171 fun getFavoriteIconHeight(): Int {
172 // Return the favorite icon height.
173 return favoriteIconHeight
176 // Reset the handlers.
177 fun resetSslErrorHandler() {
178 // Reset the current SSL error handler.
179 sslErrorHandler = null
182 fun resetHttpAuthHandler() {
183 // Reset the current HTTP authentication handler.
184 httpAuthHandler = null
188 // Pinned SSL certificates.
189 fun hasPinnedSslCertificate(): Boolean {
190 // Return the status of the pinned SSL certificate.
191 return hasPinnedSslCertificate
194 fun setPinnedSslCertificate(issuedToCName: String, issuedToOName: String, issuedToUName: String, issuedByCName: String, issuedByOName: String, issuedByUName: String, startDate: Date, endDate: Date) {
195 // Store the pinned SSL certificate information.
196 pinnedSslIssuedToCName = issuedToCName
197 pinnedSslIssuedToOName = issuedToOName
198 pinnedSslIssuedToUName = issuedToUName
199 pinnedSslIssuedByCName = issuedByCName
200 pinnedSslIssuedByOName = issuedByOName
201 pinnedSslIssuedByUName = issuedByUName
202 pinnedSslStartDate = startDate
203 pinnedSslEndDate = endDate
205 // Set the pinned SSL certificate tracker.
206 hasPinnedSslCertificate = true
209 fun getPinnedSslCertificate(): Pair<Array<String>, Array<Date>> {
210 // Create the SSL certificate string array.
211 val sslCertificateStringArray = arrayOf(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName)
213 // Create the SSL certificate date array.
214 val sslCertificateDateArray = arrayOf(pinnedSslStartDate, pinnedSslEndDate)
216 // Return the pinned SSL certificate pair.
217 return Pair(sslCertificateStringArray, sslCertificateDateArray)
220 fun clearPinnedSslCertificate() {
221 // Clear the pinned SSL certificate.
222 pinnedSslIssuedToCName = ""
223 pinnedSslIssuedToOName = ""
224 pinnedSslIssuedToUName = ""
225 pinnedSslIssuedByCName = ""
226 pinnedSslIssuedByOName = ""
227 pinnedSslIssuedByUName = ""
228 pinnedSslStartDate = Date(0)
229 pinnedSslEndDate = Date(0)
231 // Clear the pinned SSL certificate tracker.
232 hasPinnedSslCertificate = false
236 // Resource requests.
237 fun addResourceRequest(resourceRequest: Array<String>) {
238 // Add the resource request to the list.
239 resourceRequests.add(resourceRequest)
242 fun getResourceRequests(): List<Array<String>> {
243 // Return the list of resource requests as an array list.
244 return resourceRequests
247 fun clearResourceRequests() {
248 // Clear the resource requests.
249 resourceRequests.clear()
253 // Resource request counters.
254 fun incrementRequestsCount(filterList: Int) {
255 // Increment the count of the indicated filter list.
257 BLOCKED_REQUESTS -> blockedRequests++
258 EASYLIST -> easyListBlockedRequests++
259 EASYPRIVACY -> easyPrivacyBlockedRequests++
260 FANBOYS_ANNOYANCE_LIST -> fanboysAnnoyanceListBlockedRequests++
261 FANBOYS_SOCIAL_BLOCKING_LIST -> fanboysSocialBlockingListBlockedRequests++
262 ULTRALIST -> ultraListBlockedRequests++
263 ULTRAPRIVACY -> ultraPrivacyBlockedRequests++
264 THIRD_PARTY_REQUESTS -> thirdPartyBlockedRequests++
268 fun getRequestsCount(filterList: Int): Int {
269 // Return the count of the indicated filter list.
270 return when (filterList) {
271 BLOCKED_REQUESTS -> blockedRequests
272 EASYLIST -> easyListBlockedRequests
273 EASYPRIVACY -> easyPrivacyBlockedRequests
274 FANBOYS_ANNOYANCE_LIST -> fanboysAnnoyanceListBlockedRequests
275 FANBOYS_SOCIAL_BLOCKING_LIST -> fanboysSocialBlockingListBlockedRequests
276 ULTRALIST -> ultraListBlockedRequests
277 ULTRAPRIVACY -> ultraPrivacyBlockedRequests
278 THIRD_PARTY_REQUESTS -> thirdPartyBlockedRequests
279 else -> 0 // Return 0. This should never be called, but it is required by the return when statement.
283 fun resetRequestsCounters() {
284 // Reset all the resource request counters.
286 easyListBlockedRequests = 0
287 easyPrivacyBlockedRequests = 0
288 fanboysAnnoyanceListBlockedRequests = 0
289 fanboysSocialBlockingListBlockedRequests = 0
290 ultraListBlockedRequests = 0
291 ultraPrivacyBlockedRequests = 0
292 thirdPartyBlockedRequests = 0
296 // Publicly expose the scroll ranges.
297 fun getHorizontalScrollRange(): Int {
298 // Return the horizontal scroll range.
299 return computeHorizontalScrollRange()
302 fun getVerticalScrollRange(): Int {
303 // Return the vertical scroll range.
304 return computeVerticalScrollRange()
307 override fun onOverScrolled(scrollX: Int, scrollY: Int, clampedX: Boolean, clampedY: Boolean) {
308 // Run the default commands.
309 super.onOverScrolled(scrollX, scrollY, clampedX, clampedY)
311 // Display the bottom app bar if it has been hidden and the WebView was over-scrolled at the top of the screen.
312 if ((MainWebViewActivity.appBarLayout.translationY != 0f) && (scrollY == 0) && clampedY) {
313 // Animate the bottom app bar onto the screen.
314 val objectAnimator = ObjectAnimator.ofFloat(MainWebViewActivity.appBarLayout, "translationY", 0f)
317 objectAnimator.start()
322 @SuppressLint("ClickableViewAccessibility")
323 override fun onTouchEvent(motionEvent: MotionEvent): Boolean {
324 // Run the commands for the given motion event action.
325 when (motionEvent.action) {
326 MotionEvent.ACTION_DOWN -> {
327 // Start nested scrolling along the vertical axis. `ViewCompat` must be used until the minimum API >= 21.
328 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
330 // Save the current Y position. Action down will not be called again until a new motion starts.
331 previousYPosition = motionEvent.y.toInt()
333 MotionEvent.ACTION_MOVE -> {
334 // Get the current Y position.
335 val currentYMotionPosition = motionEvent.y.toInt()
337 // Calculate the pre-scroll delta Y.
338 val preScrollDeltaY = previousYPosition - currentYMotionPosition
340 // Initialize a variable to track how much of the scroll is consumed.
341 val consumedScroll = IntArray(2)
343 // Initialize a variable to track the offset in the window.
344 val offsetInWindow = IntArray(2)
346 // Get the WebView Y position.
347 val webViewYPosition = scrollY
349 // Set the scroll delta Y to initially be the same as the pre-scroll delta Y.
350 var scrollDeltaY = preScrollDeltaY
352 // Dispatch the nested pre-school. This scrolls the app bar if it needs it. `offsetInWindow` will be returned with an updated value.
353 if (dispatchNestedPreScroll(0, preScrollDeltaY, consumedScroll, offsetInWindow)) {
354 // Update the scroll delta Y if some of it was consumed.
355 scrollDeltaY = preScrollDeltaY - consumedScroll[1]
358 // Check to see if the WebView is at the top and and the scroll action is downward.
359 if (webViewYPosition == 0 && scrollDeltaY < 0) { // Swipe to refresh is being engaged.
360 // 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.
362 } else { // Swipe to refresh is not being engaged.
363 // Start the nested scroll so that the app bar can scroll off the screen.
364 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
366 // 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.
367 dispatchNestedScroll(0, scrollDeltaY, 0, 0, offsetInWindow)
369 // Store the current Y position for use in the next action move.
370 previousYPosition -= scrollDeltaY
373 else -> stopNestedScroll() // Stop nested scrolling.
376 // Perform a click. This is required by the Android accessibility guidelines.
379 // Run the default commands and return the result.
380 return super.onTouchEvent(motionEvent)
385 fun saveNestedScrollWebViewState(): Bundle {
386 // Create a saved state bundle.
387 val savedState = Bundle()
389 // Populate the saved state bundle.
390 savedState.putBoolean(ACCEPT_COOKIES, acceptCookies)
391 savedState.putBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests)
392 savedState.putString(CURRENT_DOMAIN_NAME, currentDomainName)
393 savedState.putString(CURRENT_URL, currentUrl)
394 savedState.putBoolean(DOM_STORAGE_ENABLED, this.settings.domStorageEnabled)
395 savedState.putBoolean(DOMAIN_SETTINGS_APPLIED, domainSettingsApplied)
396 savedState.putInt(DOMAIN_SETTINGS_DATABASE_ID, domainSettingsDatabaseId)
397 savedState.putBoolean(EASYLIST_ENABLED, easyListEnabled)
398 savedState.putBoolean(EASYPRIVACY_ENABLED, easyPrivacyEnabled)
399 savedState.putBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED, fanboysAnnoyanceListEnabled)
400 savedState.putBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED, fanboysSocialBlockingListEnabled)
401 savedState.putInt(FONT_SIZE, this.settings.textZoom)
402 savedState.putBoolean(HAS_PINNED_SSL_CERTIFICATE, hasPinnedSslCertificate)
403 savedState.putBoolean(IGNORE_PINNED_DOMAIN_INFORMATION, ignorePinnedDomainInformation)
404 savedState.putBoolean(JAVASCRIPT_ENABLED, this.settings.javaScriptEnabled)
405 savedState.putString(PINNED_IP_ADDRESSES, pinnedIpAddresses)
406 savedState.putLong(PINNED_SSL_END_DATE, pinnedSslEndDate.time)
407 savedState.putString(PINNED_SSL_ISSUED_BY_CNAME, pinnedSslIssuedByCName)
408 savedState.putString(PINNED_SSL_ISSUED_BY_ONAME, pinnedSslIssuedByOName)
409 savedState.putString(PINNED_SSL_ISSUED_BY_UNAME, pinnedSslIssuedByUName)
410 savedState.putString(PINNED_SSL_ISSUED_TO_CNAME, pinnedSslIssuedToCName)
411 savedState.putString(PINNED_SSL_ISSUED_TO_ONAME, pinnedSslIssuedToOName)
412 savedState.putString(PINNED_SSL_ISSUED_TO_UNAME, pinnedSslIssuedToUName)
413 savedState.putLong(PINNED_SSL_START_DATE, pinnedSslStartDate.time)
414 savedState.putBoolean(SWIPE_TO_REFRESH, swipeToRefresh)
415 savedState.putBoolean(ULTRALIST_ENABLED, ultraListEnabled)
416 savedState.putBoolean(ULTRAPRIVACY_ENABLED, ultraPrivacyEnabled)
417 savedState.putString(USER_AGENT, this.settings.userAgentString)
418 savedState.putBoolean(WIDE_VIEWPORT, this.settings.useWideViewPort)
420 // Return the saved state bundle.
424 // Restore the state.
425 fun restoreNestedScrollWebViewState(savedState: Bundle) {
426 // Restore the class variables.
427 acceptCookies = savedState.getBoolean(ACCEPT_COOKIES)
428 blockAllThirdPartyRequests = savedState.getBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS)
429 currentDomainName = savedState.getString(CURRENT_DOMAIN_NAME)!!
430 currentUrl = savedState.getString(CURRENT_URL)!!
431 this.settings.domStorageEnabled = savedState.getBoolean(DOM_STORAGE_ENABLED)
432 domainSettingsApplied = savedState.getBoolean(DOMAIN_SETTINGS_APPLIED)
433 domainSettingsDatabaseId = savedState.getInt(DOMAIN_SETTINGS_DATABASE_ID)
434 easyListEnabled = savedState.getBoolean(EASYLIST_ENABLED)
435 easyPrivacyEnabled = savedState.getBoolean(EASYPRIVACY_ENABLED)
436 fanboysAnnoyanceListEnabled = savedState.getBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED)
437 fanboysSocialBlockingListEnabled = savedState.getBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED)
438 this.settings.textZoom = savedState.getInt(FONT_SIZE)
439 hasPinnedSslCertificate = savedState.getBoolean(HAS_PINNED_SSL_CERTIFICATE)
440 ignorePinnedDomainInformation = savedState.getBoolean(IGNORE_PINNED_DOMAIN_INFORMATION)
441 this.settings.javaScriptEnabled = savedState.getBoolean(JAVASCRIPT_ENABLED)
442 pinnedIpAddresses = savedState.getString(PINNED_IP_ADDRESSES)!!
443 pinnedSslEndDate = Date(savedState.getLong(PINNED_SSL_END_DATE))
444 pinnedSslIssuedByCName = savedState.getString(PINNED_SSL_ISSUED_BY_CNAME)!!
445 pinnedSslIssuedByOName = savedState.getString(PINNED_SSL_ISSUED_BY_ONAME)!!
446 pinnedSslIssuedByUName = savedState.getString(PINNED_SSL_ISSUED_BY_UNAME)!!
447 pinnedSslIssuedToCName = savedState.getString(PINNED_SSL_ISSUED_TO_CNAME)!!
448 pinnedSslIssuedToOName = savedState.getString(PINNED_SSL_ISSUED_TO_ONAME)!!
449 pinnedSslIssuedToUName = savedState.getString(PINNED_SSL_ISSUED_TO_UNAME)!!
450 pinnedSslStartDate = Date(savedState.getLong(PINNED_SSL_START_DATE))
451 swipeToRefresh = savedState.getBoolean(SWIPE_TO_REFRESH)
452 ultraListEnabled = savedState.getBoolean(ULTRALIST_ENABLED)
453 ultraPrivacyEnabled = savedState.getBoolean(ULTRAPRIVACY_ENABLED)
454 this.settings.userAgentString = savedState.getString(USER_AGENT)
455 this.settings.useWideViewPort = savedState.getBoolean(WIDE_VIEWPORT)
459 // Method from NestedScrollingChild.
460 override fun setNestedScrollingEnabled(status: Boolean) {
461 // Set the status of the nested scrolling.
462 nestedScrollingChildHelper.isNestedScrollingEnabled = status
465 // Method from NestedScrollingChild.
466 override fun isNestedScrollingEnabled(): Boolean {
467 // Return the status of nested scrolling.
468 return nestedScrollingChildHelper.isNestedScrollingEnabled
471 // Method from NestedScrollingChild.
472 override fun startNestedScroll(axes: Int): Boolean {
473 // Start a nested scroll along the indicated axes.
474 return nestedScrollingChildHelper.startNestedScroll(axes)
477 // Method from NestedScrollingChild2.
478 override fun startNestedScroll(axes: Int, type: Int): Boolean {
479 // Start a nested scroll along the indicated axes for the given type of input which caused the scroll event.
480 return nestedScrollingChildHelper.startNestedScroll(axes, type)
483 // Method from NestedScrollingChild.
484 override fun stopNestedScroll() {
485 // Stop the nested scroll.
486 nestedScrollingChildHelper.stopNestedScroll()
489 // Method from NestedScrollingChild2.
490 override fun stopNestedScroll(type: Int) {
491 // Stop the nested scroll of the given type of input which caused the scroll event.
492 nestedScrollingChildHelper.stopNestedScroll(type)
495 // Method from NestedScrollingChild.
496 override fun hasNestedScrollingParent(): Boolean {
497 // Return the status of the nested scrolling parent.
498 return nestedScrollingChildHelper.hasNestedScrollingParent()
501 // Method from NestedScrollingChild2.
502 override fun hasNestedScrollingParent(type: Int): Boolean {
503 // return the status of the nested scrolling parent for the given type of input which caused the scroll event.
504 return nestedScrollingChildHelper.hasNestedScrollingParent(type)
507 // Method from NestedScrollingChild.
508 override fun dispatchNestedPreScroll(deltaX: Int, deltaY: Int, consumed: IntArray?, offsetInWindow: IntArray?): Boolean {
509 // Dispatch a nested pre-scroll with the specified deltas, which lets a parent to consume some of the scroll if desired.
510 return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow)
513 // Method from NestedScrollingChild2.
514 override fun dispatchNestedPreScroll(deltaX: Int, deltaY: Int, consumed: IntArray?, offsetInWindow: IntArray?, type: Int): Boolean {
515 // 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.
516 return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow, type)
519 // Method from NestedScrollingChild.
520 override fun dispatchNestedScroll(deltaXConsumed: Int, deltaYConsumed: Int, deltaXUnconsumed: Int, deltaYUnconsumed: Int, offsetInWindow: IntArray?): Boolean {
521 // Dispatch a nested scroll with the specified deltas.
522 return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow)
525 // Method from NestedScrollingChild2.
526 override fun dispatchNestedScroll(deltaXConsumed: Int, deltaYConsumed: Int, deltaXUnconsumed: Int, deltaYUnconsumed: Int, offsetInWindow: IntArray?, type: Int): Boolean {
527 // Dispatch a nested scroll with the specified deltas for the given type of input which caused the scroll event.
528 return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow, type)
531 // Method from NestedScrollingChild.
532 override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float): Boolean {
533 // Dispatch a nested pre-fling with the specified velocity, which lets a parent consume the fling if desired.
534 return nestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY)
537 // Method from NestedScrollingChild.
538 override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean): Boolean {
539 // Dispatch a nested fling with the specified velocity.
540 return nestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed)