X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Fviews%2FNestedScrollWebView.java;h=1ae819c27eace329e4c0b8a7eec111a9a3429d36;hp=ca93d6fb9df885af3a9c15e4f1720ba694bf98ce;hb=b82022327701273b1b56419e8d6042895c0bc7b9;hpb=ff636c77836983a078f33e60616204518dab61d1 diff --git a/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java b/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java index ca93d6fb..1ae819c2 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java +++ b/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Soren Stoutner . + * Copyright © 2019-2020 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -23,48 +23,97 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.util.AttributeSet; import android.view.MotionEvent; +import android.webkit.HttpAuthHandler; +import android.webkit.SslErrorHandler; import android.webkit.WebView; -import com.stoutner.privacybrowser.R; - import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.core.view.NestedScrollingChild2; import androidx.core.view.NestedScrollingChildHelper; import androidx.core.view.ViewCompat; +import com.stoutner.privacybrowser.R; + import java.util.ArrayList; +import java.util.Collections; import java.util.Date; +import java.util.List; // NestedScrollWebView extends WebView to handle nested scrolls (scrolling the app bar off the screen). public class NestedScrollWebView extends WebView implements NestedScrollingChild2 { - // These constants identify the blocklists. + // Define the blocklists constants. public final static int BLOCKED_REQUESTS = 0; - public final static int EASY_LIST = 1; - public final static int EASY_PRIVACY = 2; + public final static int EASYLIST = 1; + public final static int EASYPRIVACY = 2; public final static int FANBOYS_ANNOYANCE_LIST = 3; public final static int FANBOYS_SOCIAL_BLOCKING_LIST = 4; - public final static int ULTRA_PRIVACY = 5; - public final static int THIRD_PARTY_REQUESTS = 6; + public final static int ULTRALIST = 5; + public final static int ULTRAPRIVACY = 6; + public final static int THIRD_PARTY_REQUESTS = 7; + + // Define the saved state constants. + private final String DOMAIN_SETTINGS_APPLIED = "domain_settings_applied"; + private final String DOMAIN_SETTINGS_DATABASE_ID = "domain_settings_database_id"; + private final String CURRENT_URl = "current_url"; + private final String CURRENT_DOMAIN_NAME = "current_domain_name"; + private final String ACCEPT_FIRST_PARTY_COOKIES = "accept_first_party_cookies"; + private final String EASYLIST_ENABLED = "easylist_enabled"; + private final String EASYPRIVACY_ENABLED = "easyprivacy_enabled"; + private final String FANBOYS_ANNOYANCE_LIST_ENABLED = "fanboys_annoyance_list_enabled"; + private final String FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED = "fanboys_social_blocking_list_enabled"; + private final String ULTRALIST_ENABLED = "ultralist_enabled"; + private final String ULTRAPRIVACY_ENABLED = "ultraprivacy_enabled"; + private final String BLOCK_ALL_THIRD_PARTY_REQUESTS = "block_all_third_party_requests"; + private final String HAS_PINNED_SSL_CERTIFICATE = "has_pinned_ssl_certificate"; + private final String PINNED_SSL_ISSUED_TO_CNAME = "pinned_ssl_issued_to_cname"; + private final String PINNED_SSL_ISSUED_TO_ONAME = "pinned_ssl_issued_to_oname"; + private final String PINNED_SSL_ISSUED_TO_UNAME = "pinned_ssl_issued_to_uname"; + private final String PINNED_SSL_ISSUED_BY_CNAME = "pinned_ssl_issued_by_cname"; + private final String PINNED_SSL_ISSUED_BY_ONAME = "pinned_ssl_issued_by_oname"; + private final String PINNED_SSL_ISSUED_BY_UNAME = "pinned_ssl_issued_by_uname"; + private final String PINNED_SSL_START_DATE = "pinned_ssl_start_date"; + private final String PINNED_SSL_END_DATE = "pinned_ssl_end_date"; + private final String HAS_PINNED_IP_ADDRESSES = "has_pinned_ip_addresses"; + private final String PINNED_IP_ADDRESSES = "pinned_ip_addresses"; + private final String IGNORE_PINNED_DOMAIN_INFORMATION = "ignore_pinned_domain_information"; + private final String SWIPE_TO_REFRESH = "swipe_to_refresh"; + private final String JAVASCRIPT_ENABLED = "javascript_enabled"; + private final String DOM_STORAGE_ENABLED = "dom_storage_enabled"; + private final String USER_AGENT = "user_agent"; + private final String WIDE_VIEWPORT = "wide_viewport"; + private final String FONT_SIZE = "font_size"; // Keep a copy of the WebView fragment ID. private long webViewFragmentId; + // Store the handlers. + private SslErrorHandler sslErrorHandler; + private HttpAuthHandler httpAuthHandler; + // Track if domain settings are applied to this nested scroll WebView and, if so, the database ID. private boolean domainSettingsApplied; private int domainSettingsDatabaseId; + // Keep track of the current URL. This is used to not block resource requests to the main URL. + private String currentUrl; + // Keep track of when the domain name changes so that domain settings can be reapplied. This should never be null. private String currentDomainName = ""; + // Track the status of first-party cookies. This is necessary because first-party cookie status is app wide instead of WebView specific. + private boolean acceptFirstPartyCookies; + // Track the resource requests. - private ArrayList resourceRequests = new ArrayList<>(); + private List resourceRequests = Collections.synchronizedList(new ArrayList<>()); // Using a synchronized list makes adding resource requests thread safe. private boolean easyListEnabled; private boolean easyPrivacyEnabled; private boolean fanboysAnnoyanceListEnabled; private boolean fanboysSocialBlockingListEnabled; + private boolean ultraListEnabled; private boolean ultraPrivacyEnabled; private boolean blockAllThirdPartyRequests; private int blockedRequests; @@ -72,6 +121,7 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild private int easyPrivacyBlockedRequests; private int fanboysAnnoyanceListBlockedRequests; private int fanboysSocialBlockingListBlockedRequests; + private int ultraListBlockedRequests; private int ultraPrivacyBlockedRequests; private int thirdPartyBlockedRequests; @@ -98,7 +148,13 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild private boolean ignorePinnedDomainInformation; // The default or favorite icon. - Bitmap favoriteOrDefaultIcon; + private Bitmap favoriteOrDefaultIcon; + + // Track swipe to refresh. + private boolean swipeToRefresh; + + // Track a URL waiting for a proxy. + private String waitingForProxyUrlString = ""; // The nested scrolling child helper is used throughout the class. private NestedScrollingChildHelper nestedScrollingChildHelper; @@ -146,6 +202,40 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild } + // SSL error handler. + public void setSslErrorHandler(SslErrorHandler sslErrorHandler) { + // Store the current SSL error handler. + this.sslErrorHandler = sslErrorHandler; + } + + public SslErrorHandler getSslErrorHandler() { + // Return the current SSL error handler. + return sslErrorHandler; + } + + public void resetSslErrorHandler() { + // Reset the current SSL error handler. + sslErrorHandler = null; + } + + + // HTTP authentication handler. + public void setHttpAuthHandler(HttpAuthHandler httpAuthHandler) { + // Store the current HTTP authentication handler. + this.httpAuthHandler = httpAuthHandler; + } + + public HttpAuthHandler getHttpAuthHandler() { + // Return the current HTTP authentication handler. + return httpAuthHandler; + } + + public void resetHttpAuthHandler() { + // Reset the current HTTP authentication handler. + httpAuthHandler = null; + } + + // Domain settings. public void setDomainSettingsApplied(boolean applied) { // Store the domain settings applied status. @@ -170,6 +260,18 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild } + // Current URL. + public void setCurrentUrl(String url) { + // Store the current URL. + currentUrl = url; + } + + public String getCurrentUrl() { + // Return the current URL. + return currentUrl; + } + + // Current domain name. To function well when called, the domain name should never be allowed to be null. public void setCurrentDomainName(@NonNull String domainName) { // Store the current domain name. @@ -187,14 +289,26 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild } + // First-party cookies. + public void setAcceptFirstPartyCookies(boolean status) { + // Store the accept first-party cookies status. + acceptFirstPartyCookies = status; + } + + public boolean getAcceptFirstPartyCookies() { + // Return the accept first-party cookies status. + return acceptFirstPartyCookies; + } + + // Resource requests. public void addResourceRequest(String[] resourceRequest) { // Add the resource request to the list. resourceRequests.add(resourceRequest); } - public ArrayList getResourceRequests() { - // Return the list of resource requests. + public List getResourceRequests() { + // Return the list of resource requests as an array list. return resourceRequests; } @@ -208,12 +322,12 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild public void enableBlocklist(int blocklist, boolean status) { // Update the status of the indicated blocklist. switch (blocklist) { - case EASY_LIST: + case EASYLIST: // Update the status of the blocklist. easyListEnabled = status; break; - case EASY_PRIVACY: + case EASYPRIVACY: // Update the status of the blocklist. easyPrivacyEnabled = status; break; @@ -228,7 +342,12 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild fanboysSocialBlockingListEnabled = status; break; - case ULTRA_PRIVACY: + case ULTRALIST: + // Update the status of the blocklist. + ultraListEnabled = status; + break; + + case ULTRAPRIVACY: // Update the status of the blocklist. ultraPrivacyEnabled = status; break; @@ -243,11 +362,11 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild public boolean isBlocklistEnabled(int blocklist) { // Get the status of the indicated blocklist. switch (blocklist) { - case EASY_LIST: + case EASYLIST: // Return the status of the blocklist. return easyListEnabled; - case EASY_PRIVACY: + case EASYPRIVACY: // Return the status of the blocklist. return easyPrivacyEnabled; @@ -259,7 +378,11 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild // Return the status of the blocklist. return fanboysSocialBlockingListEnabled; - case ULTRA_PRIVACY: + case ULTRALIST: + // Return the status of the blocklist. + return ultraListEnabled; + + case ULTRAPRIVACY: // Return the status of the blocklist. return ultraPrivacyEnabled; @@ -282,6 +405,7 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild easyPrivacyBlockedRequests = 0; fanboysAnnoyanceListBlockedRequests = 0; fanboysSocialBlockingListBlockedRequests = 0; + ultraListBlockedRequests = 0; ultraPrivacyBlockedRequests = 0; thirdPartyBlockedRequests = 0; } @@ -294,12 +418,12 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild blockedRequests++; break; - case EASY_LIST: + case EASYLIST: // Increment the EasyList blocked requests count. easyListBlockedRequests++; break; - case EASY_PRIVACY: + case EASYPRIVACY: // Increment the EasyPrivacy blocked requests count. easyPrivacyBlockedRequests++; break; @@ -314,7 +438,12 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild fanboysSocialBlockingListBlockedRequests++; break; - case ULTRA_PRIVACY: + case ULTRALIST: + // Increment the UltraList blocked requests count. + ultraListBlockedRequests++; + break; + + case ULTRAPRIVACY: // Increment the UltraPrivacy blocked requests count. ultraPrivacyBlockedRequests++; break; @@ -333,11 +462,11 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild // Return the blocked requests count. return blockedRequests; - case EASY_LIST: + case EASYLIST: // Return the EasyList blocked requests count. return easyListBlockedRequests; - case EASY_PRIVACY: + case EASYPRIVACY: // Return the EasyPrivacy blocked requests count. return easyPrivacyBlockedRequests; @@ -349,7 +478,11 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild // Return the Fanboy's Social Blocking List blocked requests count. return fanboysSocialBlockingListBlockedRequests; - case ULTRA_PRIVACY: + case ULTRALIST: + // Return the UltraList blocked requests count. + return ultraListBlockedRequests; + + case ULTRAPRIVACY: // Return the UltraPrivacy blocked requests count. return ultraPrivacyBlockedRequests; @@ -475,18 +608,19 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild } - // Ignore pinned information. The syntax looks better as written, even if it is always inverted. + // Ignore pinned information. + public void setIgnorePinnedDomainInformation(boolean status) { + // Set the status of the ignore pinned domain information tracker. + ignorePinnedDomainInformation = status; + } + + // The syntax looks better as written, even if it is always inverted. @SuppressWarnings("BooleanMethodIsAlwaysInverted") public boolean ignorePinnedDomainInformation() { // Return the status of the ignore pinned domain information tracker. return ignorePinnedDomainInformation; } - public void setIgnorePinnedDomainInformation(boolean status) { - // Set the status of the ignore pinned domain information tracker. - ignorePinnedDomainInformation = status; - } - // Favorite or default icon. public void initializeFavoriteIcon() { @@ -519,6 +653,46 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild } + // Swipe to refresh. + public void setSwipeToRefresh(boolean status) { + // Store the swipe to refresh status. + swipeToRefresh = status; + } + + public boolean getSwipeToRefresh() { + // Return the swipe to refresh status. + return swipeToRefresh; + } + + + // Waiting for proxy. + public void setWaitingForProxyUrlString(String urlString) { + // Store the waiting for proxy URL string. + waitingForProxyUrlString = urlString; + } + + public String getWaitingForProxyUrlString() { + // Return the waiting for proxy URL string. + return waitingForProxyUrlString; + } + + public void resetWaitingForProxyUrlString() { + // Clear the waiting for proxy URL string. + waitingForProxyUrlString = ""; + } + + // Scroll range. + public int getHorizontalScrollRange() { + // Return the horizontal scroll range. + return computeHorizontalScrollRange(); + } + + public int getVerticalScrollRange() { + // Return the vertical scroll range. + return computeVerticalScrollRange(); + } + + @Override public boolean onTouchEvent(MotionEvent motionEvent) { @@ -560,12 +734,13 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild // Dispatch the nested pre-school. This scrolls the app bar if it needs it. `offsetInWindow` will be returned with an updated value. if (dispatchNestedPreScroll(0, preScrollDeltaY, consumedScroll, offsetInWindow)) { // Update the scroll delta Y if some of it was consumed. + // 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. scrollDeltaY = preScrollDeltaY - consumedScroll[1]; } // Check to see if the WebView is at the top and and the scroll action is downward. if ((webViewYPosition == 0) && (scrollDeltaY < 0)) { // Swipe to refresh is being engaged. - // Stop the nested scroll so that swipe to refresh has complete control. + // 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. stopNestedScroll(); } else { // Swipe to refresh is not being engaged. // Start the nested scroll so that the app bar can scroll off the screen. @@ -598,6 +773,109 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild return motionEventHandled; } + public Bundle saveNestedScrollWebViewState() { + // Create a saved state bundle. + Bundle savedState = new Bundle(); + + // Initialize the long date variables. + long pinnedSslStartDateLong = 0; + long pinnedSslEndDateLong = 0; + + // Convert the dates to longs. + if (pinnedSslStartDate != null) { + pinnedSslStartDateLong = pinnedSslStartDate.getTime(); + } + + if (pinnedSslEndDate != null) { + pinnedSslEndDateLong = pinnedSslEndDate.getTime(); + } + + // Populate the saved state bundle. + savedState.putBoolean(DOMAIN_SETTINGS_APPLIED, domainSettingsApplied); + savedState.putInt(DOMAIN_SETTINGS_DATABASE_ID, domainSettingsDatabaseId); + savedState.putString(CURRENT_URl, currentUrl); + savedState.putString(CURRENT_DOMAIN_NAME, currentDomainName); + savedState.putBoolean(ACCEPT_FIRST_PARTY_COOKIES, acceptFirstPartyCookies); + savedState.putBoolean(EASYLIST_ENABLED, easyListEnabled); + savedState.putBoolean(EASYPRIVACY_ENABLED, easyPrivacyEnabled); + savedState.putBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED, fanboysAnnoyanceListEnabled); + savedState.putBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED, fanboysSocialBlockingListEnabled); + savedState.putBoolean(ULTRALIST_ENABLED, ultraListEnabled); + savedState.putBoolean(ULTRAPRIVACY_ENABLED, ultraPrivacyEnabled); + savedState.putBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests); + savedState.putBoolean(HAS_PINNED_SSL_CERTIFICATE, hasPinnedSslCertificate); + savedState.putString(PINNED_SSL_ISSUED_TO_CNAME, pinnedSslIssuedToCName); + savedState.putString(PINNED_SSL_ISSUED_TO_ONAME, pinnedSslIssuedToOName); + savedState.putString(PINNED_SSL_ISSUED_TO_UNAME, pinnedSslIssuedToUName); + savedState.putString(PINNED_SSL_ISSUED_BY_CNAME, pinnedSslIssuedByCName); + savedState.putString(PINNED_SSL_ISSUED_BY_ONAME, pinnedSslIssuedByOName); + savedState.putString(PINNED_SSL_ISSUED_BY_UNAME, pinnedSslIssuedByUName); + savedState.putLong(PINNED_SSL_START_DATE, pinnedSslStartDateLong); + savedState.putLong(PINNED_SSL_END_DATE, pinnedSslEndDateLong); + savedState.putBoolean(HAS_PINNED_IP_ADDRESSES, hasPinnedIpAddresses); + savedState.putString(PINNED_IP_ADDRESSES, pinnedIpAddresses); + savedState.putBoolean(IGNORE_PINNED_DOMAIN_INFORMATION, ignorePinnedDomainInformation); + savedState.putBoolean(SWIPE_TO_REFRESH, swipeToRefresh); + savedState.putBoolean(JAVASCRIPT_ENABLED, this.getSettings().getJavaScriptEnabled()); + savedState.putBoolean(DOM_STORAGE_ENABLED, this.getSettings().getDomStorageEnabled()); + savedState.putString(USER_AGENT, this.getSettings().getUserAgentString()); + savedState.putBoolean(WIDE_VIEWPORT, this.getSettings().getUseWideViewPort()); + savedState.putInt(FONT_SIZE, this.getSettings().getTextZoom()); + + // Return the saved state bundle. + return savedState; + } + + public void restoreNestedScrollWebViewState(Bundle savedState) { + // Restore the class variables. + domainSettingsApplied = savedState.getBoolean(DOMAIN_SETTINGS_APPLIED); + domainSettingsDatabaseId = savedState.getInt(DOMAIN_SETTINGS_DATABASE_ID); + currentUrl = savedState.getString(CURRENT_URl); + currentDomainName = savedState.getString(CURRENT_DOMAIN_NAME); + acceptFirstPartyCookies = savedState.getBoolean(ACCEPT_FIRST_PARTY_COOKIES); + easyListEnabled = savedState.getBoolean(EASYLIST_ENABLED); + easyPrivacyEnabled = savedState.getBoolean(EASYPRIVACY_ENABLED); + fanboysAnnoyanceListEnabled = savedState.getBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED); + fanboysSocialBlockingListEnabled = savedState.getBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED); + ultraListEnabled = savedState.getBoolean(ULTRALIST_ENABLED); + ultraPrivacyEnabled = savedState.getBoolean(ULTRAPRIVACY_ENABLED); + blockAllThirdPartyRequests = savedState.getBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS); + hasPinnedSslCertificate = savedState.getBoolean(HAS_PINNED_SSL_CERTIFICATE); + pinnedSslIssuedToCName = savedState.getString(PINNED_SSL_ISSUED_TO_CNAME); + pinnedSslIssuedToOName = savedState.getString(PINNED_SSL_ISSUED_TO_ONAME); + pinnedSslIssuedToUName = savedState.getString(PINNED_SSL_ISSUED_TO_UNAME); + pinnedSslIssuedByCName = savedState.getString(PINNED_SSL_ISSUED_BY_CNAME); + pinnedSslIssuedByOName = savedState.getString(PINNED_SSL_ISSUED_BY_ONAME); + pinnedSslIssuedByUName = savedState.getString(PINNED_SSL_ISSUED_BY_UNAME); + hasPinnedIpAddresses = savedState.getBoolean(HAS_PINNED_IP_ADDRESSES); + pinnedIpAddresses = savedState.getString(PINNED_IP_ADDRESSES); + ignorePinnedDomainInformation = savedState.getBoolean(IGNORE_PINNED_DOMAIN_INFORMATION); + swipeToRefresh = savedState.getBoolean(SWIPE_TO_REFRESH); + this.getSettings().setJavaScriptEnabled(savedState.getBoolean(JAVASCRIPT_ENABLED)); + this.getSettings().setDomStorageEnabled(savedState.getBoolean(DOM_STORAGE_ENABLED)); + this.getSettings().setUserAgentString(savedState.getString(USER_AGENT)); + this.getSettings().setUseWideViewPort(savedState.getBoolean(WIDE_VIEWPORT)); + this.getSettings().setTextZoom(savedState.getInt(FONT_SIZE)); + + // Get the date longs. + long pinnedSslStartDateLong = savedState.getLong(PINNED_SSL_START_DATE); + long pinnedSslEndDateLong = savedState.getLong(PINNED_SSL_END_DATE); + + // 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. + if (pinnedSslStartDateLong == 0) { + pinnedSslStartDate = null; + } else { + pinnedSslStartDate = new Date(pinnedSslStartDateLong); + } + + // 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. + if (pinnedSslEndDateLong == 0) { + pinnedSslEndDate = null; + } else { + pinnedSslEndDate = new Date(pinnedSslEndDateLong); + } + } + // The Android accessibility guidelines require overriding `performClick()` and calling it from `onTouchEvent()`. @Override public boolean performClick() {