]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.kt
First wrong button text in View Headers in night theme. https://redmine.stoutner...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / views / NestedScrollWebView.kt
1 /*
2  * Copyright 2019-2024 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.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
33
34 import androidx.core.view.NestedScrollingChild2
35 import androidx.core.view.NestedScrollingChildHelper
36 import androidx.core.view.ViewCompat
37
38 import com.stoutner.privacybrowser.activities.MainWebViewActivity
39
40 import java.util.Collections
41 import java.util.Date
42
43 import kotlin.collections.ArrayList
44
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
54
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"
85
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 {
89
90     // Define the public variables.
91     var acceptCookies = false
92     var blockAllThirdPartyRequests = false
93     var currentDomainName = ""
94     var currentIpAddresses = ""
95     var currentUrl = ""
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
113
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
137
138     init {
139         // Enable nested scrolling by default.
140         nestedScrollingChildHelper.isNestedScrollingEnabled = true
141
142         // Initialize the favorite icon.
143         resetFavoriteIcon()
144     }
145
146     // Favorite or default icon.
147     fun resetFavoriteIcon() {
148         // Store the default icon bitmap.
149         favoriteIcon = MainWebViewActivity.defaultFavoriteIconBitmap
150
151         // Set the favorite icon height to be 0.  This way any favorite icons presented by the website will overwrite it.
152         favoriteIconHeight = 0
153     }
154
155     fun setFavoriteIcon(icon: Bitmap) {
156         // Store the current favorite icon height.
157         favoriteIconHeight = icon.height
158
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.
163             icon
164     }
165
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.
168         return favoriteIcon
169     }
170
171     fun getFavoriteIconHeight(): Int {
172         // Return the favorite icon height.
173         return favoriteIconHeight
174     }
175
176     // Reset the handlers.
177     fun resetSslErrorHandler() {
178         // Reset the current SSL error handler.
179         sslErrorHandler = null
180     }
181
182     fun resetHttpAuthHandler() {
183         // Reset the current HTTP authentication handler.
184         httpAuthHandler = null
185     }
186
187
188     // Pinned SSL certificates.
189     fun hasPinnedSslCertificate(): Boolean {
190         // Return the status of the pinned SSL certificate.
191         return hasPinnedSslCertificate
192     }
193
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
204
205         // Set the pinned SSL certificate tracker.
206         hasPinnedSslCertificate = true
207     }
208
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)
212
213         // Create the SSL certificate date array.
214         val sslCertificateDateArray = arrayOf(pinnedSslStartDate, pinnedSslEndDate)
215
216         // Return the pinned SSL certificate pair.
217         return Pair(sslCertificateStringArray, sslCertificateDateArray)
218     }
219
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)
230
231         // Clear the pinned SSL certificate tracker.
232         hasPinnedSslCertificate = false
233     }
234
235
236     // Resource requests.
237     fun addResourceRequest(resourceRequest: Array<String>) {
238         // Add the resource request to the list.
239         resourceRequests.add(resourceRequest)
240     }
241
242     fun getResourceRequests(): List<Array<String>> {
243         // Return the list of resource requests as an array list.
244         return resourceRequests
245     }
246
247     fun clearResourceRequests() {
248         // Clear the resource requests.
249         resourceRequests.clear()
250     }
251
252
253     // Resource request counters.
254     fun incrementRequestsCount(filterList: Int) {
255         // Increment the count of the indicated filter list.
256         when (filterList) {
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++
265         }
266     }
267
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.
280         }
281     }
282
283     fun resetRequestsCounters() {
284         // Reset all the resource request counters.
285         blockedRequests = 0
286         easyListBlockedRequests = 0
287         easyPrivacyBlockedRequests = 0
288         fanboysAnnoyanceListBlockedRequests = 0
289         fanboysSocialBlockingListBlockedRequests = 0
290         ultraListBlockedRequests = 0
291         ultraPrivacyBlockedRequests = 0
292         thirdPartyBlockedRequests = 0
293     }
294
295
296     // Publicly expose the scroll ranges.
297     fun getHorizontalScrollRange(): Int {
298         // Return the horizontal scroll range.
299         return computeHorizontalScrollRange()
300     }
301
302     fun getVerticalScrollRange(): Int {
303         // Return the vertical scroll range.
304         return computeVerticalScrollRange()
305     }
306
307     override fun onOverScrolled(scrollX: Int, scrollY: Int, clampedX: Boolean, clampedY: Boolean) {
308         // Run the default commands.
309         super.onOverScrolled(scrollX, scrollY, clampedX, clampedY)
310
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)
315
316             // Make it so.
317             objectAnimator.start()
318         }
319     }
320
321     // Handle touches.
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)
329
330                 // Save the current Y position.  Action down will not be called again until a new motion starts.
331                 previousYPosition = motionEvent.y.toInt()
332             }
333             MotionEvent.ACTION_MOVE -> {
334                 // Get the current Y position.
335                 val currentYMotionPosition = motionEvent.y.toInt()
336
337                 // Calculate the pre-scroll delta Y.
338                 val preScrollDeltaY = previousYPosition - currentYMotionPosition
339
340                 // Initialize a variable to track how much of the scroll is consumed.
341                 val consumedScroll = IntArray(2)
342
343                 // Initialize a variable to track the offset in the window.
344                 val offsetInWindow = IntArray(2)
345
346                 // Get the WebView Y position.
347                 val webViewYPosition = scrollY
348
349                 // Set the scroll delta Y to initially be the same as the pre-scroll delta Y.
350                 var scrollDeltaY = preScrollDeltaY
351
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]
356                 }
357
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.
361                     stopNestedScroll()
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)
365
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)
368
369                     // Store the current Y position for use in the next action move.
370                     previousYPosition -= scrollDeltaY
371                 }
372             }
373             else -> stopNestedScroll()  // Stop nested scrolling.
374         }
375
376         // Perform a click.  This is required by the Android accessibility guidelines.
377         performClick()
378
379         // Run the default commands and return the result.
380         return super.onTouchEvent(motionEvent)
381     }
382
383
384     // Save the state.
385     fun saveNestedScrollWebViewState(): Bundle {
386         // Create a saved state bundle.
387         val savedState = Bundle()
388
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)
419
420         // Return the saved state bundle.
421         return savedState
422     }
423
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)
456     }
457
458
459     // Method from NestedScrollingChild.
460     override fun setNestedScrollingEnabled(status: Boolean) {
461         // Set the status of the nested scrolling.
462         nestedScrollingChildHelper.isNestedScrollingEnabled = status
463     }
464
465     // Method from NestedScrollingChild.
466     override fun isNestedScrollingEnabled(): Boolean {
467         // Return the status of nested scrolling.
468         return nestedScrollingChildHelper.isNestedScrollingEnabled
469     }
470
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)
475     }
476
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)
481     }
482
483     // Method from NestedScrollingChild.
484     override fun stopNestedScroll() {
485         // Stop the nested scroll.
486         nestedScrollingChildHelper.stopNestedScroll()
487     }
488
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)
493     }
494
495     // Method from NestedScrollingChild.
496     override fun hasNestedScrollingParent(): Boolean {
497         // Return the status of the nested scrolling parent.
498         return nestedScrollingChildHelper.hasNestedScrollingParent()
499     }
500
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)
505     }
506
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)
511     }
512
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)
517     }
518
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)
523     }
524
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)
529     }
530
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)
535     }
536
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)
541     }
542 }