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