]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.kt
fa32cf0c1e6f0b03c1b32393d7afdf44b4e0f1a7
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / views / NestedScrollWebView.kt
1 /*
2  * Copyright 2019-2023 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 package com.stoutner.privacybrowser.views
21
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
34
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
39
40 import com.stoutner.privacybrowser.R
41 import com.stoutner.privacybrowser.activities.MainWebViewActivity
42
43 import java.util.Collections
44 import java.util.Date
45
46 import kotlin.collections.ArrayList
47
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
57
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"
88
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 {
92
93     // Define the public variables.
94     var acceptCookies = false
95     var blockAllThirdPartyRequests = false
96     var currentDomainName = ""
97     var currentIpAddresses = ""
98     var currentUrl = ""
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
116
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
140
141     init {
142         // Enable nested scrolling by default.
143         nestedScrollingChildHelper.isNestedScrollingEnabled = true
144
145         // Initialize the favorite icon.
146         initializeFavoriteIcon()
147     }
148
149     // Favorite or default icon.
150     fun initializeFavoriteIcon() {
151         // Get the default favorite icon drawable.
152         val favoriteIconDrawable = getDrawable(context, R.drawable.world)
153
154         // Cast the favorite icon drawable to a bitmap drawable.
155         val favoriteIconBitmapDrawable = (favoriteIconDrawable as BitmapDrawable?)!!
156
157         // Store the default icon bitmap.
158         favoriteIcon = favoriteIconBitmapDrawable.bitmap
159
160         // Set the favorite icon height to be 0.  This way any favorite icons presented by the website will overwrite it.
161         favoriteIconHeight = 0
162     }
163
164     fun setFavoriteIcon(icon: Bitmap) {
165         // Store the current favorite icon height.
166         favoriteIconHeight = icon.height
167
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)
171         } else {
172             // Store the icon as presented.
173             icon
174         }
175     }
176
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.
179         return favoriteIcon
180     }
181
182     fun getFavoriteIconHeight(): Int {
183         // Return the favorite icon height.
184         return favoriteIconHeight
185     }
186
187     // Reset the handlers.
188     fun resetSslErrorHandler() {
189         // Reset the current SSL error handler.
190         sslErrorHandler = null
191     }
192
193     fun resetHttpAuthHandler() {
194         // Reset the current HTTP authentication handler.
195         httpAuthHandler = null
196     }
197
198
199     // Pinned SSL certificates.
200     fun hasPinnedSslCertificate(): Boolean {
201         // Return the status of the pinned SSL certificate.
202         return hasPinnedSslCertificate
203     }
204
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
215
216         // Set the pinned SSL certificate tracker.
217         hasPinnedSslCertificate = true
218     }
219
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)
223
224         // Create the SSL certificate date array.
225         val sslCertificateDateArray = arrayOf(pinnedSslStartDate, pinnedSslEndDate)
226
227         // Return the pinned SSL certificate pair.
228         return Pair(sslCertificateStringArray, sslCertificateDateArray)
229     }
230
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)
241
242         // Clear the pinned SSL certificate tracker.
243         hasPinnedSslCertificate = false
244     }
245
246
247     // Resource requests.
248     fun addResourceRequest(resourceRequest: Array<String>) {
249         // Add the resource request to the list.
250         resourceRequests.add(resourceRequest)
251     }
252
253     fun getResourceRequests(): List<Array<String>> {
254         // Return the list of resource requests as an array list.
255         return resourceRequests
256     }
257
258     fun clearResourceRequests() {
259         // Clear the resource requests.
260         resourceRequests.clear()
261     }
262
263
264     // Resource request counters.
265     fun incrementRequestsCount(filterList: Int) {
266         // Increment the count of the indicated filter list.
267         when (filterList) {
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++
276         }
277     }
278
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.
291         }
292     }
293
294     fun resetRequestsCounters() {
295         // Reset all the resource request counters.
296         blockedRequests = 0
297         easyListBlockedRequests = 0
298         easyPrivacyBlockedRequests = 0
299         fanboysAnnoyanceListBlockedRequests = 0
300         fanboysSocialBlockingListBlockedRequests = 0
301         ultraListBlockedRequests = 0
302         ultraPrivacyBlockedRequests = 0
303         thirdPartyBlockedRequests = 0
304     }
305
306
307     // Publicly expose the scroll ranges.
308     fun getHorizontalScrollRange(): Int {
309         // Return the horizontal scroll range.
310         return computeHorizontalScrollRange()
311     }
312
313     fun getVerticalScrollRange(): Int {
314         // Return the vertical scroll range.
315         return computeVerticalScrollRange()
316     }
317
318     override fun onOverScrolled(scrollX: Int, scrollY: Int, clampedX: Boolean, clampedY: Boolean) {
319         // Run the default commands.
320         super.onOverScrolled(scrollX, scrollY, clampedX, clampedY)
321
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)
326
327             // Make it so.
328             objectAnimator.start()
329         }
330     }
331
332     // Handle touches.
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)
340
341                 // Save the current Y position.  Action down will not be called again until a new motion starts.
342                 previousYPosition = motionEvent.y.toInt()
343             }
344             MotionEvent.ACTION_MOVE -> {
345                 // Get the current Y position.
346                 val currentYMotionPosition = motionEvent.y.toInt()
347
348                 // Calculate the pre-scroll delta Y.
349                 val preScrollDeltaY = previousYPosition - currentYMotionPosition
350
351                 // Initialize a variable to track how much of the scroll is consumed.
352                 val consumedScroll = IntArray(2)
353
354                 // Initialize a variable to track the offset in the window.
355                 val offsetInWindow = IntArray(2)
356
357                 // Get the WebView Y position.
358                 val webViewYPosition = scrollY
359
360                 // Set the scroll delta Y to initially be the same as the pre-scroll delta Y.
361                 var scrollDeltaY = preScrollDeltaY
362
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]
367                 }
368
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.
372                     stopNestedScroll()
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)
376
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)
379
380                     // Store the current Y position for use in the next action move.
381                     previousYPosition -= scrollDeltaY
382                 }
383             }
384             else -> stopNestedScroll()  // Stop nested scrolling.
385         }
386
387         // Perform a click.  This is required by the Android accessibility guidelines.
388         performClick()
389
390         // Run the default commands and return the result.
391         return super.onTouchEvent(motionEvent)
392     }
393
394
395     // Save the state.
396     fun saveNestedScrollWebViewState(): Bundle {
397         // Create a saved state bundle.
398         val savedState = Bundle()
399
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)
430
431         // Return the saved state bundle.
432         return savedState
433     }
434
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)
467     }
468
469
470     // Method from NestedScrollingChild.
471     override fun setNestedScrollingEnabled(status: Boolean) {
472         // Set the status of the nested scrolling.
473         nestedScrollingChildHelper.isNestedScrollingEnabled = status
474     }
475
476     // Method from NestedScrollingChild.
477     override fun isNestedScrollingEnabled(): Boolean {
478         // Return the status of nested scrolling.
479         return nestedScrollingChildHelper.isNestedScrollingEnabled
480     }
481
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)
486     }
487
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)
492     }
493
494     // Method from NestedScrollingChild.
495     override fun stopNestedScroll() {
496         // Stop the nested scroll.
497         nestedScrollingChildHelper.stopNestedScroll()
498     }
499
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)
504     }
505
506     // Method from NestedScrollingChild.
507     override fun hasNestedScrollingParent(): Boolean {
508         // Return the status of the nested scrolling parent.
509         return nestedScrollingChildHelper.hasNestedScrollingParent()
510     }
511
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)
516     }
517
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)
522     }
523
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)
528     }
529
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)
534     }
535
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)
540     }
541
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)
546     }
547
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)
552     }
553 }