]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.kt
cd00f067cd6a68273afc113eeac2234d9033042d
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / views / NestedScrollWebView.kt
1 /*
2  * Copyright © 2019-2021 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
5  *
6  * Privacy Browser is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Privacy Browser is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package com.stoutner.privacybrowser.views
21
22 import android.annotation.SuppressLint
23 import android.content.Context
24 import android.graphics.Bitmap
25 import android.graphics.drawable.BitmapDrawable
26 import android.os.Bundle
27 import android.util.AttributeSet
28 import android.view.MotionEvent
29 import android.webkit.WebView
30 import android.webkit.SslErrorHandler
31 import android.webkit.HttpAuthHandler
32
33 import androidx.core.content.ContextCompat
34 import androidx.core.view.NestedScrollingChild2
35 import androidx.core.view.NestedScrollingChildHelper
36 import androidx.core.view.ViewCompat
37
38 import com.stoutner.privacybrowser.R
39
40 import java.util.Collections
41 import java.util.Date
42
43 import kotlin.collections.ArrayList
44
45 import kotlin.jvm.JvmOverloads
46
47 // Define the saved state constants.
48 private const val DOMAIN_SETTINGS_APPLIED = "domain_settings_applied"
49 private const val DOMAIN_SETTINGS_DATABASE_ID = "domain_settings_database_id"
50 private const val CURRENT_DOMAIN_NAME = "current_domain_name"
51 private const val CURRENT_URl = "current_url"
52 private const val ACCEPT_COOKIES = "accept_cookies"
53 private const val EASYLIST_ENABLED = "easylist_enabled"
54 private const val EASYPRIVACY_ENABLED = "easyprivacy_enabled"
55 private const val FANBOYS_ANNOYANCE_LIST_ENABLED = "fanboys_annoyance_list_enabled"
56 private const val FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED = "fanboys_social_blocking_list_enabled"
57 private const val ULTRALIST_ENABLED = "ultralist_enabled"
58 private const val ULTRAPRIVACY_ENABLED = "ultraprivacy_enabled"
59 private const val BLOCK_ALL_THIRD_PARTY_REQUESTS = "block_all_third_party_requests"
60 private const val HAS_PINNED_SSL_CERTIFICATE = "has_pinned_ssl_certificate"
61 private const val PINNED_SSL_ISSUED_TO_CNAME = "pinned_ssl_issued_to_cname"
62 private const val PINNED_SSL_ISSUED_TO_ONAME = "pinned_ssl_issued_to_oname"
63 private const val PINNED_SSL_ISSUED_TO_UNAME = "pinned_ssl_issued_to_uname"
64 private const val PINNED_SSL_ISSUED_BY_CNAME = "pinned_ssl_issued_by_cname"
65 private const val PINNED_SSL_ISSUED_BY_ONAME = "pinned_ssl_issued_by_oname"
66 private const val PINNED_SSL_ISSUED_BY_UNAME = "pinned_ssl_issued_by_uname"
67 private const val PINNED_SSL_START_DATE = "pinned_ssl_start_date"
68 private const val PINNED_SSL_END_DATE = "pinned_ssl_end_date"
69 private const val PINNED_IP_ADDRESSES = "pinned_ip_addresses"
70 private const val IGNORE_PINNED_DOMAIN_INFORMATION = "ignore_pinned_domain_information"
71 private const val SWIPE_TO_REFRESH = "swipe_to_refresh"
72 private const val JAVASCRIPT_ENABLED = "javascript_enabled"
73 private const val DOM_STORAGE_ENABLED = "dom_storage_enabled"
74 private const val USER_AGENT = "user_agent"
75 private const val WIDE_VIEWPORT = "wide_viewport"
76 private const val FONT_SIZE = "font_size"
77
78 // NestedScrollWebView extends WebView to handle nested scrolls (scrolling the app bar off the screen).  It also stores extra information about the state of the WebView used by Privacy Browser.
79 class NestedScrollWebView @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null, defaultStyle: Int = android.R.attr.webViewStyle) : WebView(context, attributeSet, defaultStyle),
80     NestedScrollingChild2 {
81
82     companion object {
83         // Define the public companion object blocklists constants.
84         const val BLOCKED_REQUESTS = 0
85         const val EASYLIST = 1
86         const val EASYPRIVACY = 2
87         const val FANBOYS_ANNOYANCE_LIST = 3
88         const val FANBOYS_SOCIAL_BLOCKING_LIST = 4
89         const val ULTRALIST = 5
90         const val ULTRAPRIVACY = 6
91         const val THIRD_PARTY_REQUESTS = 7
92     }
93
94     // Define the public variables.
95     var acceptCookies = false
96     var blockAllThirdPartyRequests = false
97     var currentDomainName = ""
98     var currentIpAddresses = ""
99     var currentUrl = ""
100     var domainSettingsApplied = false
101     var domainSettingsDatabaseId = 0
102     var easyListEnabled = true
103     var easyPrivacyEnabled = true
104     var fanboysAnnoyanceListEnabled = true
105     var fanboysSocialBlockingListEnabled = true
106     var httpAuthHandler: HttpAuthHandler? = null
107     var ignorePinnedDomainInformation = false
108     var pinnedIpAddresses = ""
109     var sslErrorHandler: SslErrorHandler? = null
110     var swipeToRefresh = false
111     var ultraListEnabled = true
112     var ultraPrivacyEnabled = true
113     var waitingForProxyUrlString = ""
114     var webViewFragmentId: Long = 0
115
116
117     // Define the private variables.
118     private val nestedScrollingChildHelper: NestedScrollingChildHelper = NestedScrollingChildHelper(this)
119     private lateinit var favoriteOrDefaultIcon: Bitmap
120     private var previousYPosition = 0  // The previous Y position needs to be tracked between motion events.
121     private var hasPinnedSslCertificate = false
122     private var pinnedSslIssuedToCName = ""
123     private var pinnedSslIssuedToOName = ""
124     private var pinnedSslIssuedToUName = ""
125     private var pinnedSslIssuedByCName = ""
126     private var pinnedSslIssuedByOName = ""
127     private var pinnedSslIssuedByUName = ""
128     private var pinnedSslStartDate = Date(0)
129     private var pinnedSslEndDate = Date(0)
130     private val resourceRequests = Collections.synchronizedList(ArrayList<Array<String>>())  // Using a synchronized list makes adding resource requests thread safe.
131     private var blockedRequests = 0
132     private var easyListBlockedRequests = 0
133     private var easyPrivacyBlockedRequests = 0
134     private var fanboysAnnoyanceListBlockedRequests = 0
135     private var fanboysSocialBlockingListBlockedRequests = 0
136     private var ultraListBlockedRequests = 0
137     private var ultraPrivacyBlockedRequests = 0
138     private var thirdPartyBlockedRequests = 0
139
140     init {
141         // Enable nested scrolling by default.
142         nestedScrollingChildHelper.isNestedScrollingEnabled = true
143
144         // Initialize the favorite icon.
145         initializeFavoriteIcon()
146     }
147
148
149     // Favorite or default icon.
150     fun initializeFavoriteIcon() {
151         // Get the default favorite icon drawable.  `ContextCompat` must be used until API >= 21.
152         val favoriteIconDrawable = ContextCompat.getDrawable(context, R.drawable.world)
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         favoriteOrDefaultIcon = favoriteIconBitmapDrawable.bitmap
159     }
160
161     fun setFavoriteOrDefaultIcon(icon: Bitmap) {
162         // Scale the favorite icon bitmap down if it is larger than 256 x 256.  Filtering uses bilinear interpolation.
163         favoriteOrDefaultIcon = if (icon.height > 256 || icon.width > 256) {
164             Bitmap.createScaledBitmap(icon, 256, 256, true)
165         } else {
166             // Store the icon as presented.
167             icon
168         }
169     }
170
171     fun getFavoriteOrDefaultIcon(): Bitmap {
172         // Return the favorite or default icon.  This is the only way to return a non-nullable variable while retaining the custom initialization and setter functions above.
173         return favoriteOrDefaultIcon
174     }
175
176
177     // Reset the handlers.
178     fun resetSslErrorHandler() {
179         // Reset the current SSL error handler.
180         sslErrorHandler = null
181     }
182
183     fun resetHttpAuthHandler() {
184         // Reset the current HTTP authentication handler.
185         httpAuthHandler = null
186     }
187
188
189     // Pinned SSL certificates.
190     fun hasPinnedSslCertificate(): Boolean {
191         // Return the status of the pinned SSL certificate.
192         return hasPinnedSslCertificate
193     }
194
195     fun setPinnedSslCertificate(issuedToCName: String, issuedToOName: String, issuedToUName: String, issuedByCName: String, issuedByOName: String, issuedByUName: String, startDate: Date, endDate: Date) {
196         // Store the pinned SSL certificate information.
197         pinnedSslIssuedToCName = issuedToCName
198         pinnedSslIssuedToOName = issuedToOName
199         pinnedSslIssuedToUName = issuedToUName
200         pinnedSslIssuedByCName = issuedByCName
201         pinnedSslIssuedByOName = issuedByOName
202         pinnedSslIssuedByUName = issuedByUName
203         pinnedSslStartDate = startDate
204         pinnedSslEndDate = endDate
205
206         // Set the pinned SSL certificate tracker.
207         hasPinnedSslCertificate = true
208     }
209
210     fun getPinnedSslCertificate(): ArrayList<Any> {
211         // Initialize an array list.
212         val arrayList = ArrayList<Any>()
213
214         // Create the SSL certificate string array.
215         val sslCertificateStringArray = arrayOf(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName)
216
217         // Create the SSL certificate date array.
218         val sslCertificateDateArray = arrayOf(pinnedSslStartDate, pinnedSslEndDate)
219
220         // Add the arrays to the array list.
221         arrayList.add(sslCertificateStringArray)
222         arrayList.add(sslCertificateDateArray)
223
224         // Return the pinned SSL certificate array list.
225         return arrayList
226     }
227
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)
238
239         // Clear the pinned SSL certificate tracker.
240         hasPinnedSslCertificate = false
241     }
242
243
244     // Resource requests.
245     fun addResourceRequest(resourceRequest: Array<String>) {
246         // Add the resource request to the list.
247         resourceRequests.add(resourceRequest)
248     }
249
250     fun getResourceRequests(): List<Array<String>> {
251         // Return the list of resource requests as an array list.
252         return resourceRequests
253     }
254
255     fun clearResourceRequests() {
256         // Clear the resource requests.
257         resourceRequests.clear()
258     }
259
260
261     // Resource request counters.
262     fun incrementRequestsCount(blocklist: Int) {
263         // Increment the count of the indicated blocklist.
264         when (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++
273         }
274     }
275
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.
288         }
289     }
290
291     fun resetRequestsCounters() {
292         // Reset all the resource request counters.
293         blockedRequests = 0
294         easyListBlockedRequests = 0
295         easyPrivacyBlockedRequests = 0
296         fanboysAnnoyanceListBlockedRequests = 0
297         fanboysSocialBlockingListBlockedRequests = 0
298         ultraListBlockedRequests = 0
299         ultraPrivacyBlockedRequests = 0
300         thirdPartyBlockedRequests = 0
301     }
302
303
304     // Publicly expose the scroll ranges.
305     fun getHorizontalScrollRange(): Int {
306         // Return the horizontal scroll range.
307         return computeHorizontalScrollRange()
308     }
309
310     fun getVerticalScrollRange(): Int {
311         // Return the vertical scroll range.
312         return computeVerticalScrollRange()
313     }
314
315
316     // Handle touches.
317     @SuppressLint("ClickableViewAccessibility")
318     override fun onTouchEvent(motionEvent: MotionEvent): Boolean {
319         // Run the commands for the given motion event action.
320         when (motionEvent.action) {
321             MotionEvent.ACTION_DOWN -> {
322                 // Start nested scrolling along the vertical axis.  `ViewCompat` must be used until the minimum API >= 21.
323                 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
324
325                 // Save the current Y position.  Action down will not be called again until a new motion starts.
326                 previousYPosition = motionEvent.y.toInt()
327             }
328             MotionEvent.ACTION_MOVE -> {
329                 // Get the current Y position.
330                 val currentYMotionPosition = motionEvent.y.toInt()
331
332                 // Calculate the pre-scroll delta Y.
333                 val preScrollDeltaY = previousYPosition - currentYMotionPosition
334
335                 // Initialize a variable to track how much of the scroll is consumed.
336                 val consumedScroll = IntArray(2)
337
338                 // Initialize a variable to track the offset in the window.
339                 val offsetInWindow = IntArray(2)
340
341                 // Get the WebView Y position.
342                 val webViewYPosition = scrollY
343
344                 // Set the scroll delta Y to initially be the same as the pre-scroll delta Y.
345                 var scrollDeltaY = preScrollDeltaY
346
347                 // Dispatch the nested pre-school.  This scrolls the app bar if it needs it.  `offsetInWindow` will be returned with an updated value.
348                 if (dispatchNestedPreScroll(0, preScrollDeltaY, consumedScroll, offsetInWindow)) {
349                     // Update the scroll delta Y if some of it was consumed.
350                     scrollDeltaY = preScrollDeltaY - consumedScroll[1]
351                 }
352
353                 // Check to see if the WebView is at the top and and the scroll action is downward.
354                 if (webViewYPosition == 0 && scrollDeltaY < 0) {  // Swipe to refresh is being engaged.
355                     // Stop the nested scroll so that swipe to refresh has complete control.  This way releasing the scroll to refresh circle doesn't scroll the WebView at the same time.
356                     stopNestedScroll()
357                 } else {  // Swipe to refresh is not being engaged.
358                     // Start the nested scroll so that the app bar can scroll off the screen.
359                     startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
360
361                     // Dispatch the nested scroll.  This scrolls the WebView.  The delta Y unconsumed normally controls the swipe refresh layout, but that is handled with the `if` statement above.
362                     dispatchNestedScroll(0, scrollDeltaY, 0, 0, offsetInWindow)
363
364                     // Store the current Y position for use in the next action move.
365                     previousYPosition -= scrollDeltaY
366                 }
367             }
368             else -> stopNestedScroll()  // Stop nested scrolling.
369         }
370
371         // Perform a click.  This is required by the Android accessibility guidelines.
372         performClick()
373
374         // Run the default commands and return the result.
375         return super.onTouchEvent(motionEvent)
376     }
377
378
379     // Save and restore state.
380     fun saveNestedScrollWebViewState(): Bundle {
381         // Create a saved state bundle.
382         val savedState = Bundle()
383
384         // Populate the saved state bundle.
385         savedState.putBoolean(DOMAIN_SETTINGS_APPLIED, domainSettingsApplied)
386         savedState.putInt(DOMAIN_SETTINGS_DATABASE_ID, domainSettingsDatabaseId)
387         savedState.putString(CURRENT_DOMAIN_NAME, currentDomainName)
388         savedState.putString(CURRENT_URl, currentUrl)
389         savedState.putBoolean(ACCEPT_COOKIES, acceptCookies)
390         savedState.putBoolean(EASYLIST_ENABLED, easyListEnabled)
391         savedState.putBoolean(EASYPRIVACY_ENABLED, easyPrivacyEnabled)
392         savedState.putBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED, fanboysAnnoyanceListEnabled)
393         savedState.putBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED, fanboysSocialBlockingListEnabled)
394         savedState.putBoolean(ULTRALIST_ENABLED, ultraListEnabled)
395         savedState.putBoolean(ULTRAPRIVACY_ENABLED, ultraPrivacyEnabled)
396         savedState.putBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests)
397         savedState.putBoolean(HAS_PINNED_SSL_CERTIFICATE, hasPinnedSslCertificate)
398         savedState.putString(PINNED_SSL_ISSUED_TO_CNAME, pinnedSslIssuedToCName)
399         savedState.putString(PINNED_SSL_ISSUED_TO_ONAME, pinnedSslIssuedToOName)
400         savedState.putString(PINNED_SSL_ISSUED_TO_UNAME, pinnedSslIssuedToUName)
401         savedState.putString(PINNED_SSL_ISSUED_BY_CNAME, pinnedSslIssuedByCName)
402         savedState.putString(PINNED_SSL_ISSUED_BY_ONAME, pinnedSslIssuedByOName)
403         savedState.putString(PINNED_SSL_ISSUED_BY_UNAME, pinnedSslIssuedByUName)
404         savedState.putLong(PINNED_SSL_START_DATE, pinnedSslStartDate.time)
405         savedState.putLong(PINNED_SSL_END_DATE, pinnedSslEndDate.time)
406         savedState.putString(PINNED_IP_ADDRESSES, pinnedIpAddresses)
407         savedState.putBoolean(IGNORE_PINNED_DOMAIN_INFORMATION, ignorePinnedDomainInformation)
408         savedState.putBoolean(SWIPE_TO_REFRESH, swipeToRefresh)
409         savedState.putBoolean(JAVASCRIPT_ENABLED, this.settings.javaScriptEnabled)
410         savedState.putBoolean(DOM_STORAGE_ENABLED, this.settings.domStorageEnabled)
411         savedState.putString(USER_AGENT, this.settings.userAgentString)
412         savedState.putBoolean(WIDE_VIEWPORT, this.settings.useWideViewPort)
413         savedState.putInt(FONT_SIZE, this.settings.textZoom)
414
415         // Return the saved state bundle.
416         return savedState
417     }
418
419     fun restoreNestedScrollWebViewState(savedState: Bundle) {
420         // Restore the class variables.
421         domainSettingsApplied = savedState.getBoolean(DOMAIN_SETTINGS_APPLIED)
422         domainSettingsDatabaseId = savedState.getInt(DOMAIN_SETTINGS_DATABASE_ID)
423         currentDomainName = savedState.getString(CURRENT_DOMAIN_NAME)!!
424         currentUrl = savedState.getString(CURRENT_URl)!!
425         acceptCookies = savedState.getBoolean(ACCEPT_COOKIES)
426         easyListEnabled = savedState.getBoolean(EASYLIST_ENABLED)
427         easyPrivacyEnabled = savedState.getBoolean(EASYPRIVACY_ENABLED)
428         fanboysAnnoyanceListEnabled = savedState.getBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED)
429         fanboysSocialBlockingListEnabled = savedState.getBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED)
430         ultraListEnabled = savedState.getBoolean(ULTRALIST_ENABLED)
431         ultraPrivacyEnabled = savedState.getBoolean(ULTRAPRIVACY_ENABLED)
432         blockAllThirdPartyRequests = savedState.getBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS)
433         hasPinnedSslCertificate = savedState.getBoolean(HAS_PINNED_SSL_CERTIFICATE)
434         pinnedSslIssuedToCName = savedState.getString(PINNED_SSL_ISSUED_TO_CNAME)!!
435         pinnedSslIssuedToOName = savedState.getString(PINNED_SSL_ISSUED_TO_ONAME)!!
436         pinnedSslIssuedToUName = savedState.getString(PINNED_SSL_ISSUED_TO_UNAME)!!
437         pinnedSslIssuedByCName = savedState.getString(PINNED_SSL_ISSUED_BY_CNAME)!!
438         pinnedSslIssuedByOName = savedState.getString(PINNED_SSL_ISSUED_BY_ONAME)!!
439         pinnedSslIssuedByUName = savedState.getString(PINNED_SSL_ISSUED_BY_UNAME)!!
440         pinnedSslStartDate = Date(savedState.getLong(PINNED_SSL_START_DATE))
441         pinnedSslEndDate = Date(savedState.getLong(PINNED_SSL_END_DATE))
442         pinnedIpAddresses = savedState.getString(PINNED_IP_ADDRESSES)!!
443         ignorePinnedDomainInformation = savedState.getBoolean(IGNORE_PINNED_DOMAIN_INFORMATION)
444         swipeToRefresh = savedState.getBoolean(SWIPE_TO_REFRESH)
445         this.settings.javaScriptEnabled = savedState.getBoolean(JAVASCRIPT_ENABLED)
446         this.settings.domStorageEnabled = savedState.getBoolean(DOM_STORAGE_ENABLED)
447         this.settings.userAgentString = savedState.getString(USER_AGENT)
448         this.settings.useWideViewPort = savedState.getBoolean(WIDE_VIEWPORT)
449         this.settings.textZoom = savedState.getInt(FONT_SIZE)
450     }
451
452
453     // Method from NestedScrollingChild.
454     override fun setNestedScrollingEnabled(status: Boolean) {
455         // Set the status of the nested scrolling.
456         nestedScrollingChildHelper.isNestedScrollingEnabled = status
457     }
458
459     // Method from NestedScrollingChild.
460     override fun isNestedScrollingEnabled(): Boolean {
461         // Return the status of nested scrolling.
462         return nestedScrollingChildHelper.isNestedScrollingEnabled
463     }
464
465     // Method from NestedScrollingChild.
466     override fun startNestedScroll(axes: Int): Boolean {
467         // Start a nested scroll along the indicated axes.
468         return nestedScrollingChildHelper.startNestedScroll(axes)
469     }
470
471     // Method from NestedScrollingChild2.
472     override fun startNestedScroll(axes: Int, type: Int): Boolean {
473         // Start a nested scroll along the indicated axes for the given type of input which caused the scroll event.
474         return nestedScrollingChildHelper.startNestedScroll(axes, type)
475     }
476
477     // Method from NestedScrollingChild.
478     override fun stopNestedScroll() {
479         // Stop the nested scroll.
480         nestedScrollingChildHelper.stopNestedScroll()
481     }
482
483     // Method from NestedScrollingChild2.
484     override fun stopNestedScroll(type: Int) {
485         // Stop the nested scroll of the given type of input which caused the scroll event.
486         nestedScrollingChildHelper.stopNestedScroll(type)
487     }
488
489     // Method from NestedScrollingChild.
490     override fun hasNestedScrollingParent(): Boolean {
491         // Return the status of the nested scrolling parent.
492         return nestedScrollingChildHelper.hasNestedScrollingParent()
493     }
494
495     // Method from NestedScrollingChild2.
496     override fun hasNestedScrollingParent(type: Int): Boolean {
497         // return the status of the nested scrolling parent for the given type of input which caused the scroll event.
498         return nestedScrollingChildHelper.hasNestedScrollingParent(type)
499     }
500
501     // Method from NestedScrollingChild.
502     override fun dispatchNestedPreScroll(deltaX: Int, deltaY: Int, consumed: IntArray?, offsetInWindow: IntArray?): Boolean {
503         // Dispatch a nested pre-scroll with the specified deltas, which lets a parent to consume some of the scroll if desired.
504         return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow)
505     }
506
507     // Method from NestedScrollingChild2.
508     override fun dispatchNestedPreScroll(deltaX: Int, deltaY: Int, consumed: IntArray?, offsetInWindow: IntArray?, type: Int): Boolean {
509         // Dispatch a nested pre-scroll with the specified deltas for the given type of input which caused the scroll event, which lets a parent to consume some of the scroll if desired.
510         return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow, type)
511     }
512
513     // Method from NestedScrollingChild.
514     override fun dispatchNestedScroll(deltaXConsumed: Int, deltaYConsumed: Int, deltaXUnconsumed: Int, deltaYUnconsumed: Int, offsetInWindow: IntArray?): Boolean {
515         // Dispatch a nested scroll with the specified deltas.
516         return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow)
517     }
518
519     // Method from NestedScrollingChild2.
520     override fun dispatchNestedScroll(deltaXConsumed: Int, deltaYConsumed: Int, deltaXUnconsumed: Int, deltaYUnconsumed: Int, offsetInWindow: IntArray?, type: Int): Boolean {
521         // Dispatch a nested scroll with the specified deltas for the given type of input which caused the scroll event.
522         return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow, type)
523     }
524
525     // Method from NestedScrollingChild.
526     override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float): Boolean {
527         // Dispatch a nested pre-fling with the specified velocity, which lets a parent consume the fling if desired.
528         return nestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY)
529     }
530
531     // Method from NestedScrollingChild.
532     override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean): Boolean {
533         // Dispatch a nested fling with the specified velocity.
534         return nestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed)
535     }
536 }