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.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.appcompat.content.res.AppCompatResources.getDrawable
35 import androidx.core.view.NestedScrollingChild2
36 import androidx.core.view.NestedScrollingChildHelper
37 import androidx.core.view.ViewCompat
39 import com.stoutner.privacybrowser.R
40 import com.stoutner.privacybrowser.activities.MainWebViewActivity
42 import java.util.Collections
45 import kotlin.collections.ArrayList
47 // Define the public constants.
48 const val BLOCKED_REQUESTS = 0
49 const val EASYLIST = 1
50 const val EASYPRIVACY = 2
51 const val FANBOYS_ANNOYANCE_LIST = 3
52 const val FANBOYS_SOCIAL_BLOCKING_LIST = 4
53 const val ULTRALIST = 5
54 const val ULTRAPRIVACY = 6
55 const val THIRD_PARTY_REQUESTS = 7
57 // Define the private class constants.
58 private const val DOMAIN_SETTINGS_APPLIED = "domain_settings_applied"
59 private const val DOMAIN_SETTINGS_DATABASE_ID = "domain_settings_database_id"
60 private const val CURRENT_DOMAIN_NAME = "current_domain_name"
61 private const val CURRENT_URl = "current_url"
62 private const val ACCEPT_COOKIES = "accept_cookies"
63 private const val EASYLIST_ENABLED = "easylist_enabled"
64 private const val EASYPRIVACY_ENABLED = "easyprivacy_enabled"
65 private const val FANBOYS_ANNOYANCE_LIST_ENABLED = "fanboys_annoyance_list_enabled"
66 private const val FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED = "fanboys_social_blocking_list_enabled"
67 private const val ULTRALIST_ENABLED = "ultralist_enabled"
68 private const val ULTRAPRIVACY_ENABLED = "ultraprivacy_enabled"
69 private const val BLOCK_ALL_THIRD_PARTY_REQUESTS = "block_all_third_party_requests"
70 private const val HAS_PINNED_SSL_CERTIFICATE = "has_pinned_ssl_certificate"
71 private const val PINNED_SSL_ISSUED_TO_CNAME = "pinned_ssl_issued_to_cname"
72 private const val PINNED_SSL_ISSUED_TO_ONAME = "pinned_ssl_issued_to_oname"
73 private const val PINNED_SSL_ISSUED_TO_UNAME = "pinned_ssl_issued_to_uname"
74 private const val PINNED_SSL_ISSUED_BY_CNAME = "pinned_ssl_issued_by_cname"
75 private const val PINNED_SSL_ISSUED_BY_ONAME = "pinned_ssl_issued_by_oname"
76 private const val PINNED_SSL_ISSUED_BY_UNAME = "pinned_ssl_issued_by_uname"
77 private const val PINNED_SSL_START_DATE = "pinned_ssl_start_date"
78 private const val PINNED_SSL_END_DATE = "pinned_ssl_end_date"
79 private const val PINNED_IP_ADDRESSES = "pinned_ip_addresses"
80 private const val IGNORE_PINNED_DOMAIN_INFORMATION = "ignore_pinned_domain_information"
81 private const val SWIPE_TO_REFRESH = "swipe_to_refresh"
82 private const val JAVASCRIPT_ENABLED = "javascript_enabled"
83 private const val DOM_STORAGE_ENABLED = "dom_storage_enabled"
84 private const val USER_AGENT = "user_agent"
85 private const val WIDE_VIEWPORT = "wide_viewport"
86 private const val FONT_SIZE = "font_size"
88 // 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.
89 class NestedScrollWebView @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null, defaultStyle: Int = android.R.attr.webViewStyle) : WebView(context, attributeSet, defaultStyle),
90 NestedScrollingChild2 {
92 // Define the public variables.
93 var acceptCookies = false
94 var blockAllThirdPartyRequests = false
95 var currentDomainName = ""
96 var currentIpAddresses = ""
98 var domainSettingsApplied = false
99 var domainSettingsDatabaseId = 0
100 var easyListEnabled = true
101 var easyPrivacyEnabled = true
102 var fanboysAnnoyanceListEnabled = true
103 var fanboysSocialBlockingListEnabled = true
104 var httpAuthHandler: HttpAuthHandler? = null
105 var ignorePinnedDomainInformation = false
106 var pinnedIpAddresses = ""
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.
143 initializeFavoriteIcon()
146 // Favorite or default icon.
147 fun initializeFavoriteIcon() {
148 // Get the default favorite icon drawable.
149 val favoriteIconDrawable = getDrawable(context, R.drawable.world)
151 // Cast the favorite icon drawable to a bitmap drawable.
152 val favoriteIconBitmapDrawable = (favoriteIconDrawable as BitmapDrawable?)!!
154 // Store the default icon bitmap.
155 favoriteIcon = favoriteIconBitmapDrawable.bitmap
157 // Set the favorite icon height to be 0. This way any favorite icons presented by the website will overwrite it.
158 favoriteIconHeight = 0
161 fun setFavoriteIcon(icon: Bitmap) {
162 // Store the current favorite icon height.
163 favoriteIconHeight = icon.height
165 // Scale the favorite icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation.
166 favoriteIcon = if (icon.height > 256 || icon.width > 256) {
167 Bitmap.createScaledBitmap(icon, 256, 256, true)
169 // Store the icon as presented.
174 fun getFavoriteIcon(): Bitmap {
175 // Return the favorite icon. This is the only way to return a non-nullable variable while retaining the custom initialization and setter functions above.
179 fun getFavoriteIconHeight(): Int {
180 // Return the favorite icon height.
181 return favoriteIconHeight
184 // Reset the handlers.
185 fun resetSslErrorHandler() {
186 // Reset the current SSL error handler.
187 sslErrorHandler = null
190 fun resetHttpAuthHandler() {
191 // Reset the current HTTP authentication handler.
192 httpAuthHandler = null
196 // Pinned SSL certificates.
197 fun hasPinnedSslCertificate(): Boolean {
198 // Return the status of the pinned SSL certificate.
199 return hasPinnedSslCertificate
202 fun setPinnedSslCertificate(issuedToCName: String, issuedToOName: String, issuedToUName: String, issuedByCName: String, issuedByOName: String, issuedByUName: String, startDate: Date, endDate: Date) {
203 // Store the pinned SSL certificate information.
204 pinnedSslIssuedToCName = issuedToCName
205 pinnedSslIssuedToOName = issuedToOName
206 pinnedSslIssuedToUName = issuedToUName
207 pinnedSslIssuedByCName = issuedByCName
208 pinnedSslIssuedByOName = issuedByOName
209 pinnedSslIssuedByUName = issuedByUName
210 pinnedSslStartDate = startDate
211 pinnedSslEndDate = endDate
213 // Set the pinned SSL certificate tracker.
214 hasPinnedSslCertificate = true
217 fun getPinnedSslCertificate(): Pair<Array<String>, Array<Date>> {
218 // Create the SSL certificate string array.
219 val sslCertificateStringArray = arrayOf(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName)
221 // Create the SSL certificate date array.
222 val sslCertificateDateArray = arrayOf(pinnedSslStartDate, pinnedSslEndDate)
224 // Return the pinned SSL certificate pair.
225 return Pair(sslCertificateStringArray, sslCertificateDateArray)
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()
315 override fun onOverScrolled(scrollX: Int, scrollY: Int, clampedX: Boolean, clampedY: Boolean) {
316 // Run the default commands.
317 super.onOverScrolled(scrollX, scrollY, clampedX, clampedY)
319 // Display the bottom app bar if it has been hidden and the WebView was over-scrolled at the top of the screen.
320 if ((MainWebViewActivity.appBarLayout.translationY != 0f) && (scrollY == 0) && clampedY) {
321 // Animate the bottom app bar onto the screen.
322 val objectAnimator = ObjectAnimator.ofFloat(MainWebViewActivity.appBarLayout, "translationY", 0f)
325 objectAnimator.start()
330 @SuppressLint("ClickableViewAccessibility")
331 override fun onTouchEvent(motionEvent: MotionEvent): Boolean {
332 // Run the commands for the given motion event action.
333 when (motionEvent.action) {
334 MotionEvent.ACTION_DOWN -> {
335 // Start nested scrolling along the vertical axis. `ViewCompat` must be used until the minimum API >= 21.
336 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
338 // Save the current Y position. Action down will not be called again until a new motion starts.
339 previousYPosition = motionEvent.y.toInt()
341 MotionEvent.ACTION_MOVE -> {
342 // Get the current Y position.
343 val currentYMotionPosition = motionEvent.y.toInt()
345 // Calculate the pre-scroll delta Y.
346 val preScrollDeltaY = previousYPosition - currentYMotionPosition
348 // Initialize a variable to track how much of the scroll is consumed.
349 val consumedScroll = IntArray(2)
351 // Initialize a variable to track the offset in the window.
352 val offsetInWindow = IntArray(2)
354 // Get the WebView Y position.
355 val webViewYPosition = scrollY
357 // Set the scroll delta Y to initially be the same as the pre-scroll delta Y.
358 var scrollDeltaY = preScrollDeltaY
360 // Dispatch the nested pre-school. This scrolls the app bar if it needs it. `offsetInWindow` will be returned with an updated value.
361 if (dispatchNestedPreScroll(0, preScrollDeltaY, consumedScroll, offsetInWindow)) {
362 // Update the scroll delta Y if some of it was consumed.
363 scrollDeltaY = preScrollDeltaY - consumedScroll[1]
366 // Check to see if the WebView is at the top and and the scroll action is downward.
367 if (webViewYPosition == 0 && scrollDeltaY < 0) { // Swipe to refresh is being engaged.
368 // 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.
370 } else { // Swipe to refresh is not being engaged.
371 // Start the nested scroll so that the app bar can scroll off the screen.
372 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
374 // 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.
375 dispatchNestedScroll(0, scrollDeltaY, 0, 0, offsetInWindow)
377 // Store the current Y position for use in the next action move.
378 previousYPosition -= scrollDeltaY
381 else -> stopNestedScroll() // Stop nested scrolling.
384 // Perform a click. This is required by the Android accessibility guidelines.
387 // Run the default commands and return the result.
388 return super.onTouchEvent(motionEvent)
393 fun saveNestedScrollWebViewState(): Bundle {
394 // Create a saved state bundle.
395 val savedState = Bundle()
397 // Populate the saved state bundle.
398 savedState.putBoolean(DOMAIN_SETTINGS_APPLIED, domainSettingsApplied)
399 savedState.putInt(DOMAIN_SETTINGS_DATABASE_ID, domainSettingsDatabaseId)
400 savedState.putString(CURRENT_DOMAIN_NAME, currentDomainName)
401 savedState.putString(CURRENT_URl, currentUrl)
402 savedState.putBoolean(ACCEPT_COOKIES, acceptCookies)
403 savedState.putBoolean(EASYLIST_ENABLED, easyListEnabled)
404 savedState.putBoolean(EASYPRIVACY_ENABLED, easyPrivacyEnabled)
405 savedState.putBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED, fanboysAnnoyanceListEnabled)
406 savedState.putBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED, fanboysSocialBlockingListEnabled)
407 savedState.putBoolean(ULTRALIST_ENABLED, ultraListEnabled)
408 savedState.putBoolean(ULTRAPRIVACY_ENABLED, ultraPrivacyEnabled)
409 savedState.putBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests)
410 savedState.putBoolean(HAS_PINNED_SSL_CERTIFICATE, hasPinnedSslCertificate)
411 savedState.putString(PINNED_SSL_ISSUED_TO_CNAME, pinnedSslIssuedToCName)
412 savedState.putString(PINNED_SSL_ISSUED_TO_ONAME, pinnedSslIssuedToOName)
413 savedState.putString(PINNED_SSL_ISSUED_TO_UNAME, pinnedSslIssuedToUName)
414 savedState.putString(PINNED_SSL_ISSUED_BY_CNAME, pinnedSslIssuedByCName)
415 savedState.putString(PINNED_SSL_ISSUED_BY_ONAME, pinnedSslIssuedByOName)
416 savedState.putString(PINNED_SSL_ISSUED_BY_UNAME, pinnedSslIssuedByUName)
417 savedState.putLong(PINNED_SSL_START_DATE, pinnedSslStartDate.time)
418 savedState.putLong(PINNED_SSL_END_DATE, pinnedSslEndDate.time)
419 savedState.putString(PINNED_IP_ADDRESSES, pinnedIpAddresses)
420 savedState.putBoolean(IGNORE_PINNED_DOMAIN_INFORMATION, ignorePinnedDomainInformation)
421 savedState.putBoolean(SWIPE_TO_REFRESH, swipeToRefresh)
422 savedState.putBoolean(JAVASCRIPT_ENABLED, this.settings.javaScriptEnabled)
423 savedState.putBoolean(DOM_STORAGE_ENABLED, this.settings.domStorageEnabled)
424 savedState.putString(USER_AGENT, this.settings.userAgentString)
425 savedState.putBoolean(WIDE_VIEWPORT, this.settings.useWideViewPort)
426 savedState.putInt(FONT_SIZE, this.settings.textZoom)
428 // Return the saved state bundle.
432 // Restore the state.
433 fun restoreNestedScrollWebViewState(savedState: Bundle) {
434 // Restore the class variables.
435 domainSettingsApplied = savedState.getBoolean(DOMAIN_SETTINGS_APPLIED)
436 domainSettingsDatabaseId = savedState.getInt(DOMAIN_SETTINGS_DATABASE_ID)
437 currentDomainName = savedState.getString(CURRENT_DOMAIN_NAME)!!
438 currentUrl = savedState.getString(CURRENT_URl)!!
439 acceptCookies = savedState.getBoolean(ACCEPT_COOKIES)
440 easyListEnabled = savedState.getBoolean(EASYLIST_ENABLED)
441 easyPrivacyEnabled = savedState.getBoolean(EASYPRIVACY_ENABLED)
442 fanboysAnnoyanceListEnabled = savedState.getBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED)
443 fanboysSocialBlockingListEnabled = savedState.getBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED)
444 ultraListEnabled = savedState.getBoolean(ULTRALIST_ENABLED)
445 ultraPrivacyEnabled = savedState.getBoolean(ULTRAPRIVACY_ENABLED)
446 blockAllThirdPartyRequests = savedState.getBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS)
447 hasPinnedSslCertificate = savedState.getBoolean(HAS_PINNED_SSL_CERTIFICATE)
448 pinnedSslIssuedToCName = savedState.getString(PINNED_SSL_ISSUED_TO_CNAME)!!
449 pinnedSslIssuedToOName = savedState.getString(PINNED_SSL_ISSUED_TO_ONAME)!!
450 pinnedSslIssuedToUName = savedState.getString(PINNED_SSL_ISSUED_TO_UNAME)!!
451 pinnedSslIssuedByCName = savedState.getString(PINNED_SSL_ISSUED_BY_CNAME)!!
452 pinnedSslIssuedByOName = savedState.getString(PINNED_SSL_ISSUED_BY_ONAME)!!
453 pinnedSslIssuedByUName = savedState.getString(PINNED_SSL_ISSUED_BY_UNAME)!!
454 pinnedSslStartDate = Date(savedState.getLong(PINNED_SSL_START_DATE))
455 pinnedSslEndDate = Date(savedState.getLong(PINNED_SSL_END_DATE))
456 pinnedIpAddresses = savedState.getString(PINNED_IP_ADDRESSES)!!
457 ignorePinnedDomainInformation = savedState.getBoolean(IGNORE_PINNED_DOMAIN_INFORMATION)
458 swipeToRefresh = savedState.getBoolean(SWIPE_TO_REFRESH)
459 this.settings.javaScriptEnabled = savedState.getBoolean(JAVASCRIPT_ENABLED)
460 this.settings.domStorageEnabled = savedState.getBoolean(DOM_STORAGE_ENABLED)
461 this.settings.userAgentString = savedState.getString(USER_AGENT)
462 this.settings.useWideViewPort = savedState.getBoolean(WIDE_VIEWPORT)
463 this.settings.textZoom = savedState.getInt(FONT_SIZE)
467 // Method from NestedScrollingChild.
468 override fun setNestedScrollingEnabled(status: Boolean) {
469 // Set the status of the nested scrolling.
470 nestedScrollingChildHelper.isNestedScrollingEnabled = status
473 // Method from NestedScrollingChild.
474 override fun isNestedScrollingEnabled(): Boolean {
475 // Return the status of nested scrolling.
476 return nestedScrollingChildHelper.isNestedScrollingEnabled
479 // Method from NestedScrollingChild.
480 override fun startNestedScroll(axes: Int): Boolean {
481 // Start a nested scroll along the indicated axes.
482 return nestedScrollingChildHelper.startNestedScroll(axes)
485 // Method from NestedScrollingChild2.
486 override fun startNestedScroll(axes: Int, type: Int): Boolean {
487 // Start a nested scroll along the indicated axes for the given type of input which caused the scroll event.
488 return nestedScrollingChildHelper.startNestedScroll(axes, type)
491 // Method from NestedScrollingChild.
492 override fun stopNestedScroll() {
493 // Stop the nested scroll.
494 nestedScrollingChildHelper.stopNestedScroll()
497 // Method from NestedScrollingChild2.
498 override fun stopNestedScroll(type: Int) {
499 // Stop the nested scroll of the given type of input which caused the scroll event.
500 nestedScrollingChildHelper.stopNestedScroll(type)
503 // Method from NestedScrollingChild.
504 override fun hasNestedScrollingParent(): Boolean {
505 // Return the status of the nested scrolling parent.
506 return nestedScrollingChildHelper.hasNestedScrollingParent()
509 // Method from NestedScrollingChild2.
510 override fun hasNestedScrollingParent(type: Int): Boolean {
511 // return the status of the nested scrolling parent for the given type of input which caused the scroll event.
512 return nestedScrollingChildHelper.hasNestedScrollingParent(type)
515 // Method from NestedScrollingChild.
516 override fun dispatchNestedPreScroll(deltaX: Int, deltaY: Int, consumed: IntArray?, offsetInWindow: IntArray?): Boolean {
517 // Dispatch a nested pre-scroll with the specified deltas, which lets a parent to consume some of the scroll if desired.
518 return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow)
521 // Method from NestedScrollingChild2.
522 override fun dispatchNestedPreScroll(deltaX: Int, deltaY: Int, consumed: IntArray?, offsetInWindow: IntArray?, type: Int): Boolean {
523 // 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.
524 return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow, type)
527 // Method from NestedScrollingChild.
528 override fun dispatchNestedScroll(deltaXConsumed: Int, deltaYConsumed: Int, deltaXUnconsumed: Int, deltaYUnconsumed: Int, offsetInWindow: IntArray?): Boolean {
529 // Dispatch a nested scroll with the specified deltas.
530 return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow)
533 // Method from NestedScrollingChild2.
534 override fun dispatchNestedScroll(deltaXConsumed: Int, deltaYConsumed: Int, deltaXUnconsumed: Int, deltaYUnconsumed: Int, offsetInWindow: IntArray?, type: Int): Boolean {
535 // Dispatch a nested scroll with the specified deltas for the given type of input which caused the scroll event.
536 return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow, type)
539 // Method from NestedScrollingChild.
540 override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float): Boolean {
541 // Dispatch a nested pre-fling with the specified velocity, which lets a parent consume the fling if desired.
542 return nestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY)
545 // Method from NestedScrollingChild.
546 override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean): Boolean {
547 // Dispatch a nested fling with the specified velocity.
548 return nestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed)