]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java
Make first-party cookies tab aware.
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / views / NestedScrollWebView.java
1 /*
2  * Copyright © 2019 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.content.Context;
23 import android.graphics.Bitmap;
24 import android.graphics.drawable.BitmapDrawable;
25 import android.graphics.drawable.Drawable;
26 import android.util.AttributeSet;
27 import android.view.MotionEvent;
28 import android.webkit.WebView;
29
30 import androidx.annotation.NonNull;
31 import androidx.core.content.ContextCompat;
32 import androidx.core.view.NestedScrollingChild2;
33 import androidx.core.view.NestedScrollingChildHelper;
34 import androidx.core.view.ViewCompat;
35
36 import com.stoutner.privacybrowser.R;
37
38 import java.util.ArrayList;
39 import java.util.Date;
40
41 // NestedScrollWebView extends WebView to handle nested scrolls (scrolling the app bar off the screen).
42 public class NestedScrollWebView extends WebView implements NestedScrollingChild2 {
43     // These constants identify the blocklists.
44     public final static int BLOCKED_REQUESTS = 0;
45     public final static int EASY_LIST = 1;
46     public final static int EASY_PRIVACY = 2;
47     public final static int FANBOYS_ANNOYANCE_LIST = 3;
48     public final static int FANBOYS_SOCIAL_BLOCKING_LIST = 4;
49     public final static int ULTRA_PRIVACY = 5;
50     public final static int THIRD_PARTY_REQUESTS = 6;
51
52     // Keep a copy of the WebView fragment ID.
53     private long webViewFragmentId;
54
55     // Track if domain settings are applied to this nested scroll WebView and, if so, the database ID.
56     private boolean domainSettingsApplied;
57     private int domainSettingsDatabaseId;
58
59     // Keep track of when the domain name changes so that domain settings can be reapplied.  This should never be null.
60     private String currentDomainName = "";
61
62     // Track the status of first-party cookies.
63     private boolean acceptFirstPartyCookies;
64
65     // Track the resource requests.
66     private ArrayList<String[]> resourceRequests = new ArrayList<>();
67     private boolean easyListEnabled;
68     private boolean easyPrivacyEnabled;
69     private boolean fanboysAnnoyanceListEnabled;
70     private boolean fanboysSocialBlockingListEnabled;
71     private boolean ultraPrivacyEnabled;
72     private boolean blockAllThirdPartyRequests;
73     private int blockedRequests;
74     private int easyListBlockedRequests;
75     private int easyPrivacyBlockedRequests;
76     private int fanboysAnnoyanceListBlockedRequests;
77     private int fanboysSocialBlockingListBlockedRequests;
78     private int ultraPrivacyBlockedRequests;
79     private int thirdPartyBlockedRequests;
80
81     // The pinned SSL certificate variables.
82     private boolean hasPinnedSslCertificate;
83     private String pinnedSslIssuedToCName;
84     private String pinnedSslIssuedToOName;
85     private String pinnedSslIssuedToUName;
86     private String pinnedSslIssuedByCName;
87     private String pinnedSslIssuedByOName;
88     private String pinnedSslIssuedByUName;
89     private Date pinnedSslStartDate;
90     private Date pinnedSslEndDate;
91
92     // The current IP addresses variables.
93     private boolean hasCurrentIpAddresses;
94     private String currentIpAddresses;
95
96     // The pinned IP addresses variables.
97     private boolean hasPinnedIpAddresses;
98     private String pinnedIpAddresses;
99
100     // The ignore pinned domain information tracker.  This is set when a user proceeds past a pinned mismatch dialog to prevent the dialog from showing again until after the domain changes.
101     private boolean ignorePinnedDomainInformation;
102
103     // The default or favorite icon.
104     Bitmap favoriteOrDefaultIcon;
105
106     // Track night mode.
107     private boolean nightMode;
108
109     // Track swipe to refresh.
110     private boolean swipeToRefresh;
111
112     // The nested scrolling child helper is used throughout the class.
113     private NestedScrollingChildHelper nestedScrollingChildHelper;
114
115     // The previous Y position needs to be tracked between motion events.
116     private int previousYPosition;
117
118
119
120     // The basic constructor.
121     public NestedScrollWebView(Context context) {
122         // Roll up to the next constructor.
123         this(context, null);
124     }
125
126     // The intermediate constructor.
127     public NestedScrollWebView(Context context, AttributeSet attributeSet) {
128         // Roll up to the next constructor.
129         this(context, attributeSet, android.R.attr.webViewStyle);
130     }
131
132     // The full constructor.
133     public NestedScrollWebView(Context context, AttributeSet attributeSet, int defaultStyle) {
134         // Run the default commands.
135         super(context, attributeSet, defaultStyle);
136
137         // Initialize the nested scrolling child helper.
138         nestedScrollingChildHelper = new NestedScrollingChildHelper(this);
139
140         // Enable nested scrolling by default.
141         nestedScrollingChildHelper.setNestedScrollingEnabled(true);
142     }
143
144
145
146     // WebView Fragment ID.
147     public void setWebViewFragmentId(long webViewFragmentId) {
148         // Store the WebView fragment ID.
149         this.webViewFragmentId = webViewFragmentId;
150     }
151
152     public long getWebViewFragmentId() {
153         // Return the WebView fragment ID.
154         return webViewFragmentId;
155     }
156
157
158     // Domain settings.
159     public void setDomainSettingsApplied(boolean applied) {
160         // Store the domain settings applied status.
161         domainSettingsApplied = applied;
162     }
163
164     public boolean getDomainSettingsApplied() {
165         // Return the domain settings applied status.
166         return domainSettingsApplied;
167     }
168
169
170     // Domain settings database ID.
171     public void setDomainSettingsDatabaseId(int databaseId) {
172         // Store the domain settings database ID.
173         domainSettingsDatabaseId = databaseId;
174     }
175
176     public int getDomainSettingsDatabaseId() {
177         // Return the domain settings database ID.
178         return domainSettingsDatabaseId;
179     }
180
181
182     // Current domain name.  To function well when called, the domain name should never be allowed to be null.
183     public void setCurrentDomainName(@NonNull String domainName) {
184         // Store the current domain name.
185         currentDomainName = domainName;
186     }
187
188     public void resetCurrentDomainName() {
189         // Reset the current domain name.
190         currentDomainName = "";
191     }
192
193     public String getCurrentDomainName() {
194         // Return the current domain name.
195         return currentDomainName;
196     }
197
198
199     // First-party cookies.
200     public void setAcceptFirstPartyCookies(boolean status) {
201         // Store the accept first-party cookies status.
202         acceptFirstPartyCookies = status;
203     }
204
205     public boolean getAcceptFirstPartyCookies() {
206         // Return the accept first-party cookies status.
207         return acceptFirstPartyCookies;
208     }
209
210
211     // Resource requests.
212     public void addResourceRequest(String[] resourceRequest) {
213         // Add the resource request to the list.
214         resourceRequests.add(resourceRequest);
215     }
216
217     public ArrayList<String[]> getResourceRequests() {
218         // Return the list of resource requests.
219         return resourceRequests;
220     }
221
222     public void clearResourceRequests() {
223         // Clear the resource requests.
224         resourceRequests.clear();
225     }
226
227
228     // Blocklists.
229     public void enableBlocklist(int blocklist, boolean status) {
230         // Update the status of the indicated blocklist.
231         switch (blocklist) {
232             case EASY_LIST:
233                 // Update the status of the blocklist.
234                 easyListEnabled = status;
235                 break;
236
237             case EASY_PRIVACY:
238                 // Update the status of the blocklist.
239                 easyPrivacyEnabled = status;
240                 break;
241
242             case FANBOYS_ANNOYANCE_LIST:
243                 // Update the status of the blocklist.
244                 fanboysAnnoyanceListEnabled = status;
245                 break;
246
247             case FANBOYS_SOCIAL_BLOCKING_LIST:
248                 // Update the status of the blocklist.
249                 fanboysSocialBlockingListEnabled = status;
250                 break;
251
252             case ULTRA_PRIVACY:
253                 // Update the status of the blocklist.
254                 ultraPrivacyEnabled = status;
255                 break;
256
257             case THIRD_PARTY_REQUESTS:
258                 // Update the status of the blocklist.
259                 blockAllThirdPartyRequests = status;
260                 break;
261         }
262     }
263
264     public boolean isBlocklistEnabled(int blocklist) {
265         // Get the status of the indicated blocklist.
266         switch (blocklist) {
267             case EASY_LIST:
268                 // Return the status of the blocklist.
269                 return easyListEnabled;
270
271             case EASY_PRIVACY:
272                 // Return the status of the blocklist.
273                 return easyPrivacyEnabled;
274
275             case FANBOYS_ANNOYANCE_LIST:
276                 // Return the status of the blocklist.
277                 return fanboysAnnoyanceListEnabled;
278
279             case FANBOYS_SOCIAL_BLOCKING_LIST:
280                 // Return the status of the blocklist.
281                 return fanboysSocialBlockingListEnabled;
282
283             case ULTRA_PRIVACY:
284                 // Return the status of the blocklist.
285                 return ultraPrivacyEnabled;
286
287             case THIRD_PARTY_REQUESTS:
288                 // Return the status of the blocklist.
289                 return blockAllThirdPartyRequests;
290
291             default:
292                 // The default value is required but should never be used.
293                 return false;
294         }
295     }
296
297
298     // Resource request counters.
299     public void resetRequestsCounters() {
300         // Reset all the resource request counters.
301         blockedRequests = 0;
302         easyListBlockedRequests = 0;
303         easyPrivacyBlockedRequests = 0;
304         fanboysAnnoyanceListBlockedRequests = 0;
305         fanboysSocialBlockingListBlockedRequests = 0;
306         ultraPrivacyBlockedRequests = 0;
307         thirdPartyBlockedRequests = 0;
308     }
309
310     public void incrementRequestsCount(int blocklist) {
311         // Increment the count of the indicated blocklist.
312         switch (blocklist) {
313             case BLOCKED_REQUESTS:
314                 // Increment the blocked requests count.
315                 blockedRequests++;
316                 break;
317
318             case EASY_LIST:
319                 // Increment the EasyList blocked requests count.
320                 easyListBlockedRequests++;
321                 break;
322
323             case EASY_PRIVACY:
324                 // Increment the EasyPrivacy blocked requests count.
325                 easyPrivacyBlockedRequests++;
326                 break;
327
328             case FANBOYS_ANNOYANCE_LIST:
329                 // Increment the Fanboy's Annoyance List blocked requests count.
330                 fanboysAnnoyanceListBlockedRequests++;
331                 break;
332
333             case FANBOYS_SOCIAL_BLOCKING_LIST:
334                 // Increment the Fanboy's Social Blocking List blocked requests count.
335                 fanboysSocialBlockingListBlockedRequests++;
336                 break;
337
338             case ULTRA_PRIVACY:
339                 // Increment the UltraPrivacy blocked requests count.
340                 ultraPrivacyBlockedRequests++;
341                 break;
342
343             case THIRD_PARTY_REQUESTS:
344                 // Increment the Third Party blocked requests count.
345                 thirdPartyBlockedRequests++;
346                 break;
347         }
348     }
349
350     public int getRequestsCount(int blocklist) {
351         // Get the count of the indicated blocklist.
352         switch (blocklist) {
353             case BLOCKED_REQUESTS:
354                 // Return the blocked requests count.
355                 return blockedRequests;
356
357             case EASY_LIST:
358                 // Return the EasyList blocked requests count.
359                 return easyListBlockedRequests;
360
361             case EASY_PRIVACY:
362                 // Return the EasyPrivacy blocked requests count.
363                 return easyPrivacyBlockedRequests;
364
365             case FANBOYS_ANNOYANCE_LIST:
366                 // Return the Fanboy's Annoyance List blocked requests count.
367                 return fanboysAnnoyanceListBlockedRequests;
368
369             case FANBOYS_SOCIAL_BLOCKING_LIST:
370                 // Return the Fanboy's Social Blocking List blocked requests count.
371                 return fanboysSocialBlockingListBlockedRequests;
372
373             case ULTRA_PRIVACY:
374                 // Return the UltraPrivacy blocked requests count.
375                 return ultraPrivacyBlockedRequests;
376
377             case THIRD_PARTY_REQUESTS:
378                 // Return the Third Party blocked requests count.
379                 return thirdPartyBlockedRequests;
380
381             default:
382                 // Return 0.  This should never end up being called.
383                 return 0;
384         }
385     }
386
387
388     // Pinned SSL certificates.
389     public boolean hasPinnedSslCertificate() {
390         // Return the status of the pinned SSL certificate.
391         return hasPinnedSslCertificate;
392     }
393
394     public void setPinnedSslCertificate(String issuedToCName, String issuedToOName, String issuedToUName, String issuedByCName, String issuedByOName, String issuedByUName, Date startDate, Date endDate) {
395         // Store the pinned SSL certificate information.
396         pinnedSslIssuedToCName = issuedToCName;
397         pinnedSslIssuedToOName = issuedToOName;
398         pinnedSslIssuedToUName = issuedToUName;
399         pinnedSslIssuedByCName = issuedByCName;
400         pinnedSslIssuedByOName = issuedByOName;
401         pinnedSslIssuedByUName = issuedByUName;
402         pinnedSslStartDate = startDate;
403         pinnedSslEndDate = endDate;
404
405         // Set the pinned SSL certificate tracker.
406         hasPinnedSslCertificate = true;
407     }
408
409     public ArrayList<Object> getPinnedSslCertificate() {
410         // Initialize an array list.
411         ArrayList<Object> arrayList = new ArrayList<>();
412
413         // Create the SSL certificate string array.
414         String[] sslCertificateStringArray = new String[] {pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName};
415
416         // Create the SSL certificate date array.
417         Date[] sslCertificateDateArray = new Date[] {pinnedSslStartDate, pinnedSslEndDate};
418
419         // Add the arrays to the array list.
420         arrayList.add(sslCertificateStringArray);
421         arrayList.add(sslCertificateDateArray);
422
423         // Return the pinned SSL certificate array list.
424         return arrayList;
425     }
426
427     public void clearPinnedSslCertificate() {
428         // Clear the pinned SSL certificate.
429         pinnedSslIssuedToCName = null;
430         pinnedSslIssuedToOName = null;
431         pinnedSslIssuedToUName = null;
432         pinnedSslIssuedByCName = null;
433         pinnedSslIssuedByOName = null;
434         pinnedSslIssuedByUName = null;
435         pinnedSslStartDate = null;
436         pinnedSslEndDate = null;
437
438         // Clear the pinned SSL certificate tracker.
439         hasPinnedSslCertificate = false;
440     }
441
442
443     // Current IP addresses.
444     public boolean hasCurrentIpAddresses() {
445         // Return the status of the current IP addresses.
446         return hasCurrentIpAddresses;
447     }
448
449     public void setCurrentIpAddresses(String ipAddresses) {
450         // Store the current IP addresses.
451         currentIpAddresses = ipAddresses;
452
453         // Set the current IP addresses tracker.
454         hasCurrentIpAddresses = true;
455     }
456
457     public String getCurrentIpAddresses() {
458         // Return the current IP addresses.
459         return currentIpAddresses;
460     }
461
462     public void clearCurrentIpAddresses() {
463         // Clear the current IP addresses.
464         currentIpAddresses = null;
465
466         // Clear the current IP addresses tracker.
467         hasCurrentIpAddresses = false;
468     }
469
470
471     // Pinned IP addresses.
472     public boolean hasPinnedIpAddresses() {
473         // Return the status of the pinned IP addresses.
474         return hasPinnedIpAddresses;
475     }
476
477     public void setPinnedIpAddresses(String ipAddresses) {
478         // Store the pinned IP addresses.
479         pinnedIpAddresses = ipAddresses;
480
481         // Set the pinned IP addresses tracker.
482         hasPinnedIpAddresses = true;
483     }
484
485     public String getPinnedIpAddresses() {
486         // Return the pinned IP addresses.
487         return pinnedIpAddresses;
488     }
489
490     public void clearPinnedIpAddresses() {
491         // Clear the pinned IP addresses.
492         pinnedIpAddresses = null;
493
494         // Clear the pinned IP addresses tracker.
495         hasPinnedIpAddresses = false;
496     }
497
498
499     // Ignore pinned information.  The syntax looks better as written, even if it is always inverted.
500     @SuppressWarnings("BooleanMethodIsAlwaysInverted")
501     public boolean ignorePinnedDomainInformation() {
502         // Return the status of the ignore pinned domain information tracker.
503         return ignorePinnedDomainInformation;
504     }
505
506     public void setIgnorePinnedDomainInformation(boolean status) {
507         // Set the status of the ignore pinned domain information tracker.
508         ignorePinnedDomainInformation = status;
509     }
510
511
512     // Favorite or default icon.
513     public void initializeFavoriteIcon() {
514         // Get the default favorite icon drawable.  `ContextCompat` must be used until API >= 21.
515         Drawable favoriteIconDrawable = ContextCompat.getDrawable(getContext(), R.drawable.world);
516
517         // Cast the favorite icon drawable to a bitmap drawable.
518         BitmapDrawable favoriteIconBitmapDrawable = (BitmapDrawable) favoriteIconDrawable;
519
520         // Remove the incorrect warning below that the favorite icon bitmap drawable might be null.
521         assert favoriteIconBitmapDrawable != null;
522
523         // Store the default icon bitmap.
524         favoriteOrDefaultIcon = favoriteIconBitmapDrawable.getBitmap();
525     }
526
527     public void setFavoriteOrDefaultIcon(Bitmap icon) {
528         // Scale the favorite icon bitmap down if it is larger than 256 x 256.  Filtering uses bilinear interpolation.
529         if ((icon.getHeight() > 256) || (icon.getWidth() > 256)) {
530             favoriteOrDefaultIcon = Bitmap.createScaledBitmap(icon, 256, 256, true);
531         } else {
532             // Store the icon as presented.
533             favoriteOrDefaultIcon = icon;
534         }
535     }
536
537     public Bitmap getFavoriteOrDefaultIcon() {
538         // Return the favorite or default icon.
539         return favoriteOrDefaultIcon;
540     }
541
542
543     // Night mode.
544     public void setNightMode(boolean status) {
545         // Store the night mode status.
546         nightMode = status;
547     }
548
549     public boolean getNightMode() {
550         // Return the night mode status.
551         return nightMode;
552     }
553
554
555     // Swipe to refresh.
556     public void setSwipeToRefresh(boolean status) {
557         // Store the swipe to refresh status.
558         swipeToRefresh = status;
559     }
560
561     public boolean getSwipeToRefresh() {
562         // Return the swipe to refresh status.
563         return swipeToRefresh;
564     }
565
566
567
568     @Override
569     public boolean onTouchEvent(MotionEvent motionEvent) {
570         // Initialize a tracker to return if this motion event is handled.
571         boolean motionEventHandled;
572
573         // Run the commands for the given motion event action.
574         switch (motionEvent.getAction()) {
575             case MotionEvent.ACTION_DOWN:
576                 // Start nested scrolling along the vertical axis.  `ViewCompat` must be used until the minimum API >= 21.
577                 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
578
579                 // Save the current Y position.  Action down will not be called again until a new motion starts.
580                 previousYPosition = (int) motionEvent.getY();
581
582                 // Run the default commands.
583                 motionEventHandled = super.onTouchEvent(motionEvent);
584                 break;
585
586             case MotionEvent.ACTION_MOVE:
587                 // Get the current Y position.
588                 int currentYMotionPosition = (int) motionEvent.getY();
589
590                 // Calculate the pre-scroll delta Y.
591                 int preScrollDeltaY = previousYPosition - currentYMotionPosition;
592
593                 // Initialize a variable to track how much of the scroll is consumed.
594                 int[] consumedScroll = new int[2];
595
596                 // Initialize a variable to track the offset in the window.
597                 int[] offsetInWindow = new int[2];
598
599                 // Get the WebView Y position.
600                 int webViewYPosition = getScrollY();
601
602                 // Set the scroll delta Y to initially be the same as the pre-scroll delta Y.
603                 int scrollDeltaY = preScrollDeltaY;
604
605                 // Dispatch the nested pre-school.  This scrolls the app bar if it needs it.  `offsetInWindow` will be returned with an updated value.
606                 if (dispatchNestedPreScroll(0, preScrollDeltaY, consumedScroll, offsetInWindow)) {
607                     // Update the scroll delta Y if some of it was consumed.
608                     // There is currently a bug in Android where if scrolling up at a certain slow speed the input can lock the pre scroll and continue to consume it after the app bar is fully displayed.
609                     scrollDeltaY = preScrollDeltaY - consumedScroll[1];
610                 }
611
612                 // Check to see if the WebView is at the top and and the scroll action is downward.
613                 if ((webViewYPosition == 0) && (scrollDeltaY < 0)) {  // Swipe to refresh is being engaged.
614                     // 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.
615                     stopNestedScroll();
616                 } else {  // Swipe to refresh is not being engaged.
617                     // Start the nested scroll so that the app bar can scroll off the screen.
618                     startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
619
620                     // 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.
621                     dispatchNestedScroll(0, scrollDeltaY, 0, 0, offsetInWindow);
622
623                     // Store the current Y position for use in the next action move.
624                     previousYPosition = previousYPosition - scrollDeltaY;
625                 }
626
627                 // Run the default commands.
628                 motionEventHandled = super.onTouchEvent(motionEvent);
629                 break;
630
631
632             default:
633                 // Stop nested scrolling.
634                 stopNestedScroll();
635
636                 // Run the default commands.
637                 motionEventHandled = super.onTouchEvent(motionEvent);
638         }
639
640         // Perform a click.  This is required by the Android accessibility guidelines.
641         performClick();
642
643         // Return the status of the motion event.
644         return motionEventHandled;
645     }
646
647     // The Android accessibility guidelines require overriding `performClick()` and calling it from `onTouchEvent()`.
648     @Override
649     public boolean performClick() {
650         return super.performClick();
651     }
652
653
654     // Method from NestedScrollingChild.
655     @Override
656     public void setNestedScrollingEnabled(boolean status) {
657         // Set the status of the nested scrolling.
658         nestedScrollingChildHelper.setNestedScrollingEnabled(status);
659     }
660
661     // Method from NestedScrollingChild.
662     @Override
663     public boolean isNestedScrollingEnabled() {
664         // Return the status of nested scrolling.
665         return nestedScrollingChildHelper.isNestedScrollingEnabled();
666     }
667
668
669     // Method from NestedScrollingChild.
670     @Override
671     public boolean startNestedScroll(int axes) {
672         // Start a nested scroll along the indicated axes.
673         return nestedScrollingChildHelper.startNestedScroll(axes);
674     }
675
676     // Method from NestedScrollingChild2.
677     @Override
678     public boolean startNestedScroll(int axes, int type) {
679         // Start a nested scroll along the indicated axes for the given type of input which caused the scroll event.
680         return nestedScrollingChildHelper.startNestedScroll(axes, type);
681     }
682
683
684     // Method from NestedScrollingChild.
685     @Override
686     public void stopNestedScroll() {
687         // Stop the nested scroll.
688         nestedScrollingChildHelper.stopNestedScroll();
689     }
690
691     // Method from NestedScrollingChild2.
692     @Override
693     public void stopNestedScroll(int type) {
694         // Stop the nested scroll of the given type of input which caused the scroll event.
695         nestedScrollingChildHelper.stopNestedScroll(type);
696     }
697
698
699     // Method from NestedScrollingChild.
700     @Override
701     public boolean hasNestedScrollingParent() {
702         // Return the status of the nested scrolling parent.
703         return nestedScrollingChildHelper.hasNestedScrollingParent();
704     }
705
706     // Method from NestedScrollingChild2.
707     @Override
708     public boolean hasNestedScrollingParent(int type) {
709         // return the status of the nested scrolling parent for the given type of input which caused the scroll event.
710         return nestedScrollingChildHelper.hasNestedScrollingParent(type);
711     }
712
713
714     // Method from NestedScrollingChild.
715     @Override
716     public boolean dispatchNestedPreScroll(int deltaX, int deltaY, int[] consumed, int[] offsetInWindow) {
717         // Dispatch a nested pre-scroll with the specified deltas, which lets a parent to consume some of the scroll if desired.
718         return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow);
719     }
720
721     // Method from NestedScrollingChild2.
722     @Override
723     public boolean dispatchNestedPreScroll(int deltaX, int deltaY, int[] consumed, int[] offsetInWindow, int type) {
724         // 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.
725         return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow, type);
726     }
727
728
729     // Method from NestedScrollingChild.
730     @Override
731     public boolean dispatchNestedScroll(int deltaXConsumed, int deltaYConsumed, int deltaXUnconsumed, int deltaYUnconsumed, int[] offsetInWindow) {
732         // Dispatch a nested scroll with the specified deltas.
733         return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow);
734     }
735
736     // Method from NestedScrollingChild2.
737     @Override
738     public boolean dispatchNestedScroll(int deltaXConsumed, int deltaYConsumed, int deltaXUnconsumed, int deltaYUnconsumed, int[] offsetInWindow, int type) {
739         // Dispatch a nested scroll with the specified deltas for the given type of input which caused the scroll event.
740         return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow, type);
741     }
742
743
744     // Method from NestedScrollingChild.
745     @Override
746     public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
747         // Dispatch a nested pre-fling with the specified velocity, which lets a parent consume the fling if desired.
748         return nestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
749     }
750
751     // Method from NestedScrollingChild.
752     @Override
753     public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
754         // Dispatch a nested fling with the specified velocity.
755         return nestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
756     }
757 }