2 * Copyright © 2019-2020 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
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.
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.
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/>.
20 package com.stoutner.privacybrowser.views;
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.os.Bundle;
27 import android.util.AttributeSet;
28 import android.view.MotionEvent;
29 import android.webkit.HttpAuthHandler;
30 import android.webkit.SslErrorHandler;
31 import android.webkit.WebView;
33 import androidx.annotation.NonNull;
34 import androidx.core.content.ContextCompat;
35 import androidx.core.view.NestedScrollingChild2;
36 import androidx.core.view.NestedScrollingChildHelper;
37 import androidx.core.view.ViewCompat;
39 import com.stoutner.privacybrowser.R;
41 import java.util.ArrayList;
42 import java.util.Collections;
43 import java.util.Date;
44 import java.util.List;
46 // NestedScrollWebView extends WebView to handle nested scrolls (scrolling the app bar off the screen).
47 public class NestedScrollWebView extends WebView implements NestedScrollingChild2 {
48 // Define the blocklists constants.
49 public final static int BLOCKED_REQUESTS = 0;
50 public final static int EASYLIST = 1;
51 public final static int EASYPRIVACY = 2;
52 public final static int FANBOYS_ANNOYANCE_LIST = 3;
53 public final static int FANBOYS_SOCIAL_BLOCKING_LIST = 4;
54 public final static int ULTRALIST = 5;
55 public final static int ULTRAPRIVACY = 6;
56 public final static int THIRD_PARTY_REQUESTS = 7;
58 // Define the saved state constants.
59 private final String DOMAIN_SETTINGS_APPLIED = "domain_settings_applied";
60 private final String DOMAIN_SETTINGS_DATABASE_ID = "domain_settings_database_id";
61 private final String CURRENT_URl = "current_url";
62 private final String CURRENT_DOMAIN_NAME = "current_domain_name";
63 private final String ACCEPT_FIRST_PARTY_COOKIES = "accept_first_party_cookies";
64 private final String EASYLIST_ENABLED = "easylist_enabled";
65 private final String EASYPRIVACY_ENABLED = "easyprivacy_enabled";
66 private final String FANBOYS_ANNOYANCE_LIST_ENABLED = "fanboys_annoyance_list_enabled";
67 private final String FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED = "fanboys_social_blocking_list_enabled";
68 private final String ULTRALIST_ENABLED = "ultralist_enabled";
69 private final String ULTRAPRIVACY_ENABLED = "ultraprivacy_enabled";
70 private final String BLOCK_ALL_THIRD_PARTY_REQUESTS = "block_all_third_party_requests";
71 private final String HAS_PINNED_SSL_CERTIFICATE = "has_pinned_ssl_certificate";
72 private final String PINNED_SSL_ISSUED_TO_CNAME = "pinned_ssl_issued_to_cname";
73 private final String PINNED_SSL_ISSUED_TO_ONAME = "pinned_ssl_issued_to_oname";
74 private final String PINNED_SSL_ISSUED_TO_UNAME = "pinned_ssl_issued_to_uname";
75 private final String PINNED_SSL_ISSUED_BY_CNAME = "pinned_ssl_issued_by_cname";
76 private final String PINNED_SSL_ISSUED_BY_ONAME = "pinned_ssl_issued_by_oname";
77 private final String PINNED_SSL_ISSUED_BY_UNAME = "pinned_ssl_issued_by_uname";
78 private final String PINNED_SSL_START_DATE = "pinned_ssl_start_date";
79 private final String PINNED_SSL_END_DATE = "pinned_ssl_end_date";
80 private final String HAS_PINNED_IP_ADDRESSES = "has_pinned_ip_addresses";
81 private final String PINNED_IP_ADDRESSES = "pinned_ip_addresses";
82 private final String IGNORE_PINNED_DOMAIN_INFORMATION = "ignore_pinned_domain_information";
83 private final String SWIPE_TO_REFRESH = "swipe_to_refresh";
84 private final String JAVASCRIPT_ENABLED = "javascript_enabled";
85 private final String DOM_STORAGE_ENABLED = "dom_storage_enabled";
86 private final String USER_AGENT = "user_agent";
87 private final String WIDE_VIEWPORT = "wide_viewport";
88 private final String FONT_SIZE = "font_size";
90 // Keep a copy of the WebView fragment ID.
91 private long webViewFragmentId;
93 // Store the handlers.
94 private SslErrorHandler sslErrorHandler;
95 private HttpAuthHandler httpAuthHandler;
97 // Track if domain settings are applied to this nested scroll WebView and, if so, the database ID.
98 private boolean domainSettingsApplied;
99 private int domainSettingsDatabaseId;
101 // Keep track of the current URL. This is used to not block resource requests to the main URL.
102 private String currentUrl;
104 // Keep track of when the domain name changes so that domain settings can be reapplied. This should never be null.
105 private String currentDomainName = "";
107 // Track the status of first-party cookies. This is necessary because first-party cookie status is app wide instead of WebView specific.
108 private boolean acceptFirstPartyCookies;
110 // Track the resource requests.
111 private List<String[]> resourceRequests = Collections.synchronizedList(new ArrayList<>()); // Using a synchronized list makes adding resource requests thread safe.
112 private boolean easyListEnabled;
113 private boolean easyPrivacyEnabled;
114 private boolean fanboysAnnoyanceListEnabled;
115 private boolean fanboysSocialBlockingListEnabled;
116 private boolean ultraListEnabled;
117 private boolean ultraPrivacyEnabled;
118 private boolean blockAllThirdPartyRequests;
119 private int blockedRequests;
120 private int easyListBlockedRequests;
121 private int easyPrivacyBlockedRequests;
122 private int fanboysAnnoyanceListBlockedRequests;
123 private int fanboysSocialBlockingListBlockedRequests;
124 private int ultraListBlockedRequests;
125 private int ultraPrivacyBlockedRequests;
126 private int thirdPartyBlockedRequests;
128 // The pinned SSL certificate variables.
129 private boolean hasPinnedSslCertificate;
130 private String pinnedSslIssuedToCName;
131 private String pinnedSslIssuedToOName;
132 private String pinnedSslIssuedToUName;
133 private String pinnedSslIssuedByCName;
134 private String pinnedSslIssuedByOName;
135 private String pinnedSslIssuedByUName;
136 private Date pinnedSslStartDate;
137 private Date pinnedSslEndDate;
139 // The current IP addresses variables.
140 private boolean hasCurrentIpAddresses;
141 private String currentIpAddresses;
143 // The pinned IP addresses variables.
144 private boolean hasPinnedIpAddresses;
145 private String pinnedIpAddresses;
147 // 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.
148 private boolean ignorePinnedDomainInformation;
150 // The default or favorite icon.
151 private Bitmap favoriteOrDefaultIcon;
153 // Track swipe to refresh.
154 private boolean swipeToRefresh;
156 // Track a URL waiting for a proxy.
157 private String waitingForProxyUrlString = "";
159 // The nested scrolling child helper is used throughout the class.
160 private NestedScrollingChildHelper nestedScrollingChildHelper;
162 // The previous Y position needs to be tracked between motion events.
163 private int previousYPosition;
167 // The basic constructor.
168 public NestedScrollWebView(Context context) {
169 // Roll up to the next constructor.
173 // The intermediate constructor.
174 public NestedScrollWebView(Context context, AttributeSet attributeSet) {
175 // Roll up to the next constructor.
176 this(context, attributeSet, android.R.attr.webViewStyle);
179 // The full constructor.
180 public NestedScrollWebView(Context context, AttributeSet attributeSet, int defaultStyle) {
181 // Run the default commands.
182 super(context, attributeSet, defaultStyle);
184 // Initialize the nested scrolling child helper.
185 nestedScrollingChildHelper = new NestedScrollingChildHelper(this);
187 // Enable nested scrolling by default.
188 nestedScrollingChildHelper.setNestedScrollingEnabled(true);
193 // WebView Fragment ID.
194 public void setWebViewFragmentId(long webViewFragmentId) {
195 // Store the WebView fragment ID.
196 this.webViewFragmentId = webViewFragmentId;
199 public long getWebViewFragmentId() {
200 // Return the WebView fragment ID.
201 return webViewFragmentId;
205 // SSL error handler.
206 public void setSslErrorHandler(SslErrorHandler sslErrorHandler) {
207 // Store the current SSL error handler.
208 this.sslErrorHandler = sslErrorHandler;
211 public SslErrorHandler getSslErrorHandler() {
212 // Return the current SSL error handler.
213 return sslErrorHandler;
216 public void resetSslErrorHandler() {
217 // Reset the current SSL error handler.
218 sslErrorHandler = null;
222 // HTTP authentication handler.
223 public void setHttpAuthHandler(HttpAuthHandler httpAuthHandler) {
224 // Store the current HTTP authentication handler.
225 this.httpAuthHandler = httpAuthHandler;
228 public HttpAuthHandler getHttpAuthHandler() {
229 // Return the current HTTP authentication handler.
230 return httpAuthHandler;
233 public void resetHttpAuthHandler() {
234 // Reset the current HTTP authentication handler.
235 httpAuthHandler = null;
240 public void setDomainSettingsApplied(boolean applied) {
241 // Store the domain settings applied status.
242 domainSettingsApplied = applied;
245 public boolean getDomainSettingsApplied() {
246 // Return the domain settings applied status.
247 return domainSettingsApplied;
251 // Domain settings database ID.
252 public void setDomainSettingsDatabaseId(int databaseId) {
253 // Store the domain settings database ID.
254 domainSettingsDatabaseId = databaseId;
257 public int getDomainSettingsDatabaseId() {
258 // Return the domain settings database ID.
259 return domainSettingsDatabaseId;
264 public void setCurrentUrl(String url) {
265 // Store the current URL.
269 public String getCurrentUrl() {
270 // Return the current URL.
275 // Current domain name. To function well when called, the domain name should never be allowed to be null.
276 public void setCurrentDomainName(@NonNull String domainName) {
277 // Store the current domain name.
278 currentDomainName = domainName;
281 public void resetCurrentDomainName() {
282 // Reset the current domain name.
283 currentDomainName = "";
286 public String getCurrentDomainName() {
287 // Return the current domain name.
288 return currentDomainName;
292 // First-party cookies.
293 public void setAcceptFirstPartyCookies(boolean status) {
294 // Store the accept first-party cookies status.
295 acceptFirstPartyCookies = status;
298 public boolean getAcceptFirstPartyCookies() {
299 // Return the accept first-party cookies status.
300 return acceptFirstPartyCookies;
304 // Resource requests.
305 public void addResourceRequest(String[] resourceRequest) {
306 // Add the resource request to the list.
307 resourceRequests.add(resourceRequest);
310 public List<String[]> getResourceRequests() {
311 // Return the list of resource requests as an array list.
312 return resourceRequests;
315 public void clearResourceRequests() {
316 // Clear the resource requests.
317 resourceRequests.clear();
322 public void enableBlocklist(int blocklist, boolean status) {
323 // Update the status of the indicated blocklist.
326 // Update the status of the blocklist.
327 easyListEnabled = status;
331 // Update the status of the blocklist.
332 easyPrivacyEnabled = status;
335 case FANBOYS_ANNOYANCE_LIST:
336 // Update the status of the blocklist.
337 fanboysAnnoyanceListEnabled = status;
340 case FANBOYS_SOCIAL_BLOCKING_LIST:
341 // Update the status of the blocklist.
342 fanboysSocialBlockingListEnabled = status;
346 // Update the status of the blocklist.
347 ultraListEnabled = status;
351 // Update the status of the blocklist.
352 ultraPrivacyEnabled = status;
355 case THIRD_PARTY_REQUESTS:
356 // Update the status of the blocklist.
357 blockAllThirdPartyRequests = status;
362 public boolean isBlocklistEnabled(int blocklist) {
363 // Get the status of the indicated blocklist.
366 // Return the status of the blocklist.
367 return easyListEnabled;
370 // Return the status of the blocklist.
371 return easyPrivacyEnabled;
373 case FANBOYS_ANNOYANCE_LIST:
374 // Return the status of the blocklist.
375 return fanboysAnnoyanceListEnabled;
377 case FANBOYS_SOCIAL_BLOCKING_LIST:
378 // Return the status of the blocklist.
379 return fanboysSocialBlockingListEnabled;
382 // Return the status of the blocklist.
383 return ultraListEnabled;
386 // Return the status of the blocklist.
387 return ultraPrivacyEnabled;
389 case THIRD_PARTY_REQUESTS:
390 // Return the status of the blocklist.
391 return blockAllThirdPartyRequests;
394 // The default value is required but should never be used.
400 // Resource request counters.
401 public void resetRequestsCounters() {
402 // Reset all the resource request counters.
404 easyListBlockedRequests = 0;
405 easyPrivacyBlockedRequests = 0;
406 fanboysAnnoyanceListBlockedRequests = 0;
407 fanboysSocialBlockingListBlockedRequests = 0;
408 ultraListBlockedRequests = 0;
409 ultraPrivacyBlockedRequests = 0;
410 thirdPartyBlockedRequests = 0;
413 public void incrementRequestsCount(int blocklist) {
414 // Increment the count of the indicated blocklist.
416 case BLOCKED_REQUESTS:
417 // Increment the blocked requests count.
422 // Increment the EasyList blocked requests count.
423 easyListBlockedRequests++;
427 // Increment the EasyPrivacy blocked requests count.
428 easyPrivacyBlockedRequests++;
431 case FANBOYS_ANNOYANCE_LIST:
432 // Increment the Fanboy's Annoyance List blocked requests count.
433 fanboysAnnoyanceListBlockedRequests++;
436 case FANBOYS_SOCIAL_BLOCKING_LIST:
437 // Increment the Fanboy's Social Blocking List blocked requests count.
438 fanboysSocialBlockingListBlockedRequests++;
442 // Increment the UltraList blocked requests count.
443 ultraListBlockedRequests++;
447 // Increment the UltraPrivacy blocked requests count.
448 ultraPrivacyBlockedRequests++;
451 case THIRD_PARTY_REQUESTS:
452 // Increment the Third Party blocked requests count.
453 thirdPartyBlockedRequests++;
458 public int getRequestsCount(int blocklist) {
459 // Get the count of the indicated blocklist.
461 case BLOCKED_REQUESTS:
462 // Return the blocked requests count.
463 return blockedRequests;
466 // Return the EasyList blocked requests count.
467 return easyListBlockedRequests;
470 // Return the EasyPrivacy blocked requests count.
471 return easyPrivacyBlockedRequests;
473 case FANBOYS_ANNOYANCE_LIST:
474 // Return the Fanboy's Annoyance List blocked requests count.
475 return fanboysAnnoyanceListBlockedRequests;
477 case FANBOYS_SOCIAL_BLOCKING_LIST:
478 // Return the Fanboy's Social Blocking List blocked requests count.
479 return fanboysSocialBlockingListBlockedRequests;
482 // Return the UltraList blocked requests count.
483 return ultraListBlockedRequests;
486 // Return the UltraPrivacy blocked requests count.
487 return ultraPrivacyBlockedRequests;
489 case THIRD_PARTY_REQUESTS:
490 // Return the Third Party blocked requests count.
491 return thirdPartyBlockedRequests;
494 // Return 0. This should never end up being called.
500 // Pinned SSL certificates.
501 public boolean hasPinnedSslCertificate() {
502 // Return the status of the pinned SSL certificate.
503 return hasPinnedSslCertificate;
506 public void setPinnedSslCertificate(String issuedToCName, String issuedToOName, String issuedToUName, String issuedByCName, String issuedByOName, String issuedByUName, Date startDate, Date endDate) {
507 // Store the pinned SSL certificate information.
508 pinnedSslIssuedToCName = issuedToCName;
509 pinnedSslIssuedToOName = issuedToOName;
510 pinnedSslIssuedToUName = issuedToUName;
511 pinnedSslIssuedByCName = issuedByCName;
512 pinnedSslIssuedByOName = issuedByOName;
513 pinnedSslIssuedByUName = issuedByUName;
514 pinnedSslStartDate = startDate;
515 pinnedSslEndDate = endDate;
517 // Set the pinned SSL certificate tracker.
518 hasPinnedSslCertificate = true;
521 public ArrayList<Object> getPinnedSslCertificate() {
522 // Initialize an array list.
523 ArrayList<Object> arrayList = new ArrayList<>();
525 // Create the SSL certificate string array.
526 String[] sslCertificateStringArray = new String[] {pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName};
528 // Create the SSL certificate date array.
529 Date[] sslCertificateDateArray = new Date[] {pinnedSslStartDate, pinnedSslEndDate};
531 // Add the arrays to the array list.
532 arrayList.add(sslCertificateStringArray);
533 arrayList.add(sslCertificateDateArray);
535 // Return the pinned SSL certificate array list.
539 public void clearPinnedSslCertificate() {
540 // Clear the pinned SSL certificate.
541 pinnedSslIssuedToCName = null;
542 pinnedSslIssuedToOName = null;
543 pinnedSslIssuedToUName = null;
544 pinnedSslIssuedByCName = null;
545 pinnedSslIssuedByOName = null;
546 pinnedSslIssuedByUName = null;
547 pinnedSslStartDate = null;
548 pinnedSslEndDate = null;
550 // Clear the pinned SSL certificate tracker.
551 hasPinnedSslCertificate = false;
555 // Current IP addresses.
556 public boolean hasCurrentIpAddresses() {
557 // Return the status of the current IP addresses.
558 return hasCurrentIpAddresses;
561 public void setCurrentIpAddresses(String ipAddresses) {
562 // Store the current IP addresses.
563 currentIpAddresses = ipAddresses;
565 // Set the current IP addresses tracker.
566 hasCurrentIpAddresses = true;
569 public String getCurrentIpAddresses() {
570 // Return the current IP addresses.
571 return currentIpAddresses;
574 public void clearCurrentIpAddresses() {
575 // Clear the current IP addresses.
576 currentIpAddresses = null;
578 // Clear the current IP addresses tracker.
579 hasCurrentIpAddresses = false;
583 // Pinned IP addresses.
584 public boolean hasPinnedIpAddresses() {
585 // Return the status of the pinned IP addresses.
586 return hasPinnedIpAddresses;
589 public void setPinnedIpAddresses(String ipAddresses) {
590 // Store the pinned IP addresses.
591 pinnedIpAddresses = ipAddresses;
593 // Set the pinned IP addresses tracker.
594 hasPinnedIpAddresses = true;
597 public String getPinnedIpAddresses() {
598 // Return the pinned IP addresses.
599 return pinnedIpAddresses;
602 public void clearPinnedIpAddresses() {
603 // Clear the pinned IP addresses.
604 pinnedIpAddresses = null;
606 // Clear the pinned IP addresses tracker.
607 hasPinnedIpAddresses = false;
611 // Ignore pinned information.
612 public void setIgnorePinnedDomainInformation(boolean status) {
613 // Set the status of the ignore pinned domain information tracker.
614 ignorePinnedDomainInformation = status;
617 // The syntax looks better as written, even if it is always inverted.
618 @SuppressWarnings("BooleanMethodIsAlwaysInverted")
619 public boolean ignorePinnedDomainInformation() {
620 // Return the status of the ignore pinned domain information tracker.
621 return ignorePinnedDomainInformation;
625 // Favorite or default icon.
626 public void initializeFavoriteIcon() {
627 // Get the default favorite icon drawable. `ContextCompat` must be used until API >= 21.
628 Drawable favoriteIconDrawable = ContextCompat.getDrawable(getContext(), R.drawable.world);
630 // Cast the favorite icon drawable to a bitmap drawable.
631 BitmapDrawable favoriteIconBitmapDrawable = (BitmapDrawable) favoriteIconDrawable;
633 // Remove the incorrect warning below that the favorite icon bitmap drawable might be null.
634 assert favoriteIconBitmapDrawable != null;
636 // Store the default icon bitmap.
637 favoriteOrDefaultIcon = favoriteIconBitmapDrawable.getBitmap();
640 public void setFavoriteOrDefaultIcon(Bitmap icon) {
641 // Scale the favorite icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation.
642 if ((icon.getHeight() > 256) || (icon.getWidth() > 256)) {
643 favoriteOrDefaultIcon = Bitmap.createScaledBitmap(icon, 256, 256, true);
645 // Store the icon as presented.
646 favoriteOrDefaultIcon = icon;
650 public Bitmap getFavoriteOrDefaultIcon() {
651 // Return the favorite or default icon.
652 return favoriteOrDefaultIcon;
657 public void setSwipeToRefresh(boolean status) {
658 // Store the swipe to refresh status.
659 swipeToRefresh = status;
662 public boolean getSwipeToRefresh() {
663 // Return the swipe to refresh status.
664 return swipeToRefresh;
668 // Waiting for proxy.
669 public void setWaitingForProxyUrlString(String urlString) {
670 // Store the waiting for proxy URL string.
671 waitingForProxyUrlString = urlString;
674 public String getWaitingForProxyUrlString() {
675 // Return the waiting for proxy URL string.
676 return waitingForProxyUrlString;
679 public void resetWaitingForProxyUrlString() {
680 // Clear the waiting for proxy URL string.
681 waitingForProxyUrlString = "";
685 public int getHorizontalScrollRange() {
686 // Return the horizontal scroll range.
687 return computeHorizontalScrollRange();
690 public int getVerticalScrollRange() {
691 // Return the vertical scroll range.
692 return computeVerticalScrollRange();
698 public boolean onTouchEvent(MotionEvent motionEvent) {
699 // Initialize a tracker to return if this motion event is handled.
700 boolean motionEventHandled;
702 // Run the commands for the given motion event action.
703 switch (motionEvent.getAction()) {
704 case MotionEvent.ACTION_DOWN:
705 // Start nested scrolling along the vertical axis. `ViewCompat` must be used until the minimum API >= 21.
706 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
708 // Save the current Y position. Action down will not be called again until a new motion starts.
709 previousYPosition = (int) motionEvent.getY();
711 // Run the default commands.
712 motionEventHandled = super.onTouchEvent(motionEvent);
715 case MotionEvent.ACTION_MOVE:
716 // Get the current Y position.
717 int currentYMotionPosition = (int) motionEvent.getY();
719 // Calculate the pre-scroll delta Y.
720 int preScrollDeltaY = previousYPosition - currentYMotionPosition;
722 // Initialize a variable to track how much of the scroll is consumed.
723 int[] consumedScroll = new int[2];
725 // Initialize a variable to track the offset in the window.
726 int[] offsetInWindow = new int[2];
728 // Get the WebView Y position.
729 int webViewYPosition = getScrollY();
731 // Set the scroll delta Y to initially be the same as the pre-scroll delta Y.
732 int scrollDeltaY = preScrollDeltaY;
734 // Dispatch the nested pre-school. This scrolls the app bar if it needs it. `offsetInWindow` will be returned with an updated value.
735 if (dispatchNestedPreScroll(0, preScrollDeltaY, consumedScroll, offsetInWindow)) {
736 // Update the scroll delta Y if some of it was consumed.
737 // 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.
738 scrollDeltaY = preScrollDeltaY - consumedScroll[1];
741 // Check to see if the WebView is at the top and and the scroll action is downward.
742 if ((webViewYPosition == 0) && (scrollDeltaY < 0)) { // Swipe to refresh is being engaged.
743 // 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.
745 } else { // Swipe to refresh is not being engaged.
746 // Start the nested scroll so that the app bar can scroll off the screen.
747 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
749 // 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.
750 dispatchNestedScroll(0, scrollDeltaY, 0, 0, offsetInWindow);
752 // Store the current Y position for use in the next action move.
753 previousYPosition = previousYPosition - scrollDeltaY;
756 // Run the default commands.
757 motionEventHandled = super.onTouchEvent(motionEvent);
762 // Stop nested scrolling.
765 // Run the default commands.
766 motionEventHandled = super.onTouchEvent(motionEvent);
769 // Perform a click. This is required by the Android accessibility guidelines.
772 // Return the status of the motion event.
773 return motionEventHandled;
776 public Bundle saveNestedScrollWebViewState() {
777 // Create a saved state bundle.
778 Bundle savedState = new Bundle();
780 // Initialize the long date variables.
781 long pinnedSslStartDateLong = 0;
782 long pinnedSslEndDateLong = 0;
784 // Convert the dates to longs.
785 if (pinnedSslStartDate != null) {
786 pinnedSslStartDateLong = pinnedSslStartDate.getTime();
789 if (pinnedSslEndDate != null) {
790 pinnedSslEndDateLong = pinnedSslEndDate.getTime();
793 // Populate the saved state bundle.
794 savedState.putBoolean(DOMAIN_SETTINGS_APPLIED, domainSettingsApplied);
795 savedState.putInt(DOMAIN_SETTINGS_DATABASE_ID, domainSettingsDatabaseId);
796 savedState.putString(CURRENT_URl, currentUrl);
797 savedState.putString(CURRENT_DOMAIN_NAME, currentDomainName);
798 savedState.putBoolean(ACCEPT_FIRST_PARTY_COOKIES, acceptFirstPartyCookies);
799 savedState.putBoolean(EASYLIST_ENABLED, easyListEnabled);
800 savedState.putBoolean(EASYPRIVACY_ENABLED, easyPrivacyEnabled);
801 savedState.putBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED, fanboysAnnoyanceListEnabled);
802 savedState.putBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED, fanboysSocialBlockingListEnabled);
803 savedState.putBoolean(ULTRALIST_ENABLED, ultraListEnabled);
804 savedState.putBoolean(ULTRAPRIVACY_ENABLED, ultraPrivacyEnabled);
805 savedState.putBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests);
806 savedState.putBoolean(HAS_PINNED_SSL_CERTIFICATE, hasPinnedSslCertificate);
807 savedState.putString(PINNED_SSL_ISSUED_TO_CNAME, pinnedSslIssuedToCName);
808 savedState.putString(PINNED_SSL_ISSUED_TO_ONAME, pinnedSslIssuedToOName);
809 savedState.putString(PINNED_SSL_ISSUED_TO_UNAME, pinnedSslIssuedToUName);
810 savedState.putString(PINNED_SSL_ISSUED_BY_CNAME, pinnedSslIssuedByCName);
811 savedState.putString(PINNED_SSL_ISSUED_BY_ONAME, pinnedSslIssuedByOName);
812 savedState.putString(PINNED_SSL_ISSUED_BY_UNAME, pinnedSslIssuedByUName);
813 savedState.putLong(PINNED_SSL_START_DATE, pinnedSslStartDateLong);
814 savedState.putLong(PINNED_SSL_END_DATE, pinnedSslEndDateLong);
815 savedState.putBoolean(HAS_PINNED_IP_ADDRESSES, hasPinnedIpAddresses);
816 savedState.putString(PINNED_IP_ADDRESSES, pinnedIpAddresses);
817 savedState.putBoolean(IGNORE_PINNED_DOMAIN_INFORMATION, ignorePinnedDomainInformation);
818 savedState.putBoolean(SWIPE_TO_REFRESH, swipeToRefresh);
819 savedState.putBoolean(JAVASCRIPT_ENABLED, this.getSettings().getJavaScriptEnabled());
820 savedState.putBoolean(DOM_STORAGE_ENABLED, this.getSettings().getDomStorageEnabled());
821 savedState.putString(USER_AGENT, this.getSettings().getUserAgentString());
822 savedState.putBoolean(WIDE_VIEWPORT, this.getSettings().getUseWideViewPort());
823 savedState.putInt(FONT_SIZE, this.getSettings().getTextZoom());
825 // Return the saved state bundle.
829 public void restoreNestedScrollWebViewState(Bundle savedState) {
830 // Restore the class variables.
831 domainSettingsApplied = savedState.getBoolean(DOMAIN_SETTINGS_APPLIED);
832 domainSettingsDatabaseId = savedState.getInt(DOMAIN_SETTINGS_DATABASE_ID);
833 currentUrl = savedState.getString(CURRENT_URl);
834 currentDomainName = savedState.getString(CURRENT_DOMAIN_NAME);
835 acceptFirstPartyCookies = savedState.getBoolean(ACCEPT_FIRST_PARTY_COOKIES);
836 easyListEnabled = savedState.getBoolean(EASYLIST_ENABLED);
837 easyPrivacyEnabled = savedState.getBoolean(EASYPRIVACY_ENABLED);
838 fanboysAnnoyanceListEnabled = savedState.getBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED);
839 fanboysSocialBlockingListEnabled = savedState.getBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED);
840 ultraListEnabled = savedState.getBoolean(ULTRALIST_ENABLED);
841 ultraPrivacyEnabled = savedState.getBoolean(ULTRAPRIVACY_ENABLED);
842 blockAllThirdPartyRequests = savedState.getBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS);
843 hasPinnedSslCertificate = savedState.getBoolean(HAS_PINNED_SSL_CERTIFICATE);
844 pinnedSslIssuedToCName = savedState.getString(PINNED_SSL_ISSUED_TO_CNAME);
845 pinnedSslIssuedToOName = savedState.getString(PINNED_SSL_ISSUED_TO_ONAME);
846 pinnedSslIssuedToUName = savedState.getString(PINNED_SSL_ISSUED_TO_UNAME);
847 pinnedSslIssuedByCName = savedState.getString(PINNED_SSL_ISSUED_BY_CNAME);
848 pinnedSslIssuedByOName = savedState.getString(PINNED_SSL_ISSUED_BY_ONAME);
849 pinnedSslIssuedByUName = savedState.getString(PINNED_SSL_ISSUED_BY_UNAME);
850 hasPinnedIpAddresses = savedState.getBoolean(HAS_PINNED_IP_ADDRESSES);
851 pinnedIpAddresses = savedState.getString(PINNED_IP_ADDRESSES);
852 ignorePinnedDomainInformation = savedState.getBoolean(IGNORE_PINNED_DOMAIN_INFORMATION);
853 swipeToRefresh = savedState.getBoolean(SWIPE_TO_REFRESH);
854 this.getSettings().setJavaScriptEnabled(savedState.getBoolean(JAVASCRIPT_ENABLED));
855 this.getSettings().setDomStorageEnabled(savedState.getBoolean(DOM_STORAGE_ENABLED));
856 this.getSettings().setUserAgentString(savedState.getString(USER_AGENT));
857 this.getSettings().setUseWideViewPort(savedState.getBoolean(WIDE_VIEWPORT));
858 this.getSettings().setTextZoom(savedState.getInt(FONT_SIZE));
860 // Get the date longs.
861 long pinnedSslStartDateLong = savedState.getLong(PINNED_SSL_START_DATE);
862 long pinnedSslEndDateLong = savedState.getLong(PINNED_SSL_END_DATE);
864 // Set the pinned SSL start date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0.
865 if (pinnedSslStartDateLong == 0) {
866 pinnedSslStartDate = null;
868 pinnedSslStartDate = new Date(pinnedSslStartDateLong);
871 // Set the Pinned SSL end date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0.
872 if (pinnedSslEndDateLong == 0) {
873 pinnedSslEndDate = null;
875 pinnedSslEndDate = new Date(pinnedSslEndDateLong);
879 // The Android accessibility guidelines require overriding `performClick()` and calling it from `onTouchEvent()`.
881 public boolean performClick() {
882 return super.performClick();
886 // Method from NestedScrollingChild.
888 public void setNestedScrollingEnabled(boolean status) {
889 // Set the status of the nested scrolling.
890 nestedScrollingChildHelper.setNestedScrollingEnabled(status);
893 // Method from NestedScrollingChild.
895 public boolean isNestedScrollingEnabled() {
896 // Return the status of nested scrolling.
897 return nestedScrollingChildHelper.isNestedScrollingEnabled();
901 // Method from NestedScrollingChild.
903 public boolean startNestedScroll(int axes) {
904 // Start a nested scroll along the indicated axes.
905 return nestedScrollingChildHelper.startNestedScroll(axes);
908 // Method from NestedScrollingChild2.
910 public boolean startNestedScroll(int axes, int type) {
911 // Start a nested scroll along the indicated axes for the given type of input which caused the scroll event.
912 return nestedScrollingChildHelper.startNestedScroll(axes, type);
916 // Method from NestedScrollingChild.
918 public void stopNestedScroll() {
919 // Stop the nested scroll.
920 nestedScrollingChildHelper.stopNestedScroll();
923 // Method from NestedScrollingChild2.
925 public void stopNestedScroll(int type) {
926 // Stop the nested scroll of the given type of input which caused the scroll event.
927 nestedScrollingChildHelper.stopNestedScroll(type);
931 // Method from NestedScrollingChild.
933 public boolean hasNestedScrollingParent() {
934 // Return the status of the nested scrolling parent.
935 return nestedScrollingChildHelper.hasNestedScrollingParent();
938 // Method from NestedScrollingChild2.
940 public boolean hasNestedScrollingParent(int type) {
941 // return the status of the nested scrolling parent for the given type of input which caused the scroll event.
942 return nestedScrollingChildHelper.hasNestedScrollingParent(type);
946 // Method from NestedScrollingChild.
948 public boolean dispatchNestedPreScroll(int deltaX, int deltaY, int[] consumed, int[] offsetInWindow) {
949 // Dispatch a nested pre-scroll with the specified deltas, which lets a parent to consume some of the scroll if desired.
950 return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow);
953 // Method from NestedScrollingChild2.
955 public boolean dispatchNestedPreScroll(int deltaX, int deltaY, int[] consumed, int[] offsetInWindow, int type) {
956 // 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.
957 return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow, type);
961 // Method from NestedScrollingChild.
963 public boolean dispatchNestedScroll(int deltaXConsumed, int deltaYConsumed, int deltaXUnconsumed, int deltaYUnconsumed, int[] offsetInWindow) {
964 // Dispatch a nested scroll with the specified deltas.
965 return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow);
968 // Method from NestedScrollingChild2.
970 public boolean dispatchNestedScroll(int deltaXConsumed, int deltaYConsumed, int deltaXUnconsumed, int deltaYUnconsumed, int[] offsetInWindow, int type) {
971 // Dispatch a nested scroll with the specified deltas for the given type of input which caused the scroll event.
972 return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow, type);
976 // Method from NestedScrollingChild.
978 public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
979 // Dispatch a nested pre-fling with the specified velocity, which lets a parent consume the fling if desired.
980 return nestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
983 // Method from NestedScrollingChild.
985 public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
986 // Dispatch a nested fling with the specified velocity.
987 return nestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);