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