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=f7d57b68065e7da54c39e25b2680bd29700e079c;hp=ea94571aa834a6924e4d79b7f1b6c40fe2b789d4;hb=fe788514a50a591f9722ededc13e608ceb268bb8;hpb=ba40295dffd761ccdc95d3b46ca7acbad1f00d5e 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 ea94571a..f7d57b68 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java +++ b/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java @@ -24,12 +24,73 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.webkit.WebView; +import androidx.annotation.NonNull; import androidx.core.view.NestedScrollingChild2; import androidx.core.view.NestedScrollingChildHelper; import androidx.core.view.ViewCompat; +import java.util.ArrayList; +import java.util.Date; + // 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. + 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 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; + + // Keep a copy of the WebView fragment ID. + private long webViewFragmentId; + + // 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 when the domain name changes so that domain settings can be reapplied. This should never be null. + private String currentDomainName = ""; + + // Track the resource requests. + private ArrayList resourceRequests = new ArrayList<>(); + private boolean easyListEnabled; + private boolean easyPrivacyEnabled; + private boolean fanboysAnnoyanceListEnabled; + private boolean fanboysSocialBlockingListEnabled; + private boolean ultraPrivacyEnabled; + private boolean blockAllThirdPartyRequests; + private int blockedRequests; + private int easyListBlockedRequests; + private int easyPrivacyBlockedRequests; + private int fanboysAnnoyanceListBlockedRequests; + private int fanboysSocialBlockingListBlockedRequests; + private int ultraPrivacyBlockedRequests; + private int thirdPartyBlockedRequests; + + // The pinned SSL certificate variables. + private boolean hasPinnedSslCertificate; + private String pinnedSslIssuedToCName; + private String pinnedSslIssuedToOName; + private String pinnedSslIssuedToUName; + private String pinnedSslIssuedByCName; + private String pinnedSslIssuedByOName; + private String pinnedSslIssuedByUName; + private Date pinnedSslStartDate; + private Date pinnedSslEndDate; + + // The current IP addresses variables. + private boolean hasCurrentIpAddresses; + private String currentIpAddresses; + + // The pinned IP addresses variables. + private boolean hasPinnedIpAddresses; + private String pinnedIpAddresses; + + // 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. + private boolean ignorePinnedDomainInformation; + // The nested scrolling child helper is used throughout the class. private NestedScrollingChildHelper nestedScrollingChildHelper; @@ -37,19 +98,20 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild private int previousYPosition; - // Basic constructor. + + // The basic constructor. public NestedScrollWebView(Context context) { // Roll up to the next constructor. this(context, null); } - // Intermediate constructor. + // The intermediate constructor. public NestedScrollWebView(Context context, AttributeSet attributeSet) { // Roll up to the next constructor. this(context, attributeSet, android.R.attr.webViewStyle); } - // Full constructor. + // The full constructor. public NestedScrollWebView(Context context, AttributeSet attributeSet, int defaultStyle) { // Run the default commands. super(context, attributeSet, defaultStyle); @@ -62,6 +124,362 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild } + + // WebView Fragment ID. + public void setWebViewFragmentId(long webViewFragmentId) { + // Store the WebView fragment ID. + this.webViewFragmentId = webViewFragmentId; + } + + public long getWebViewFragmentId() { + // Return the WebView fragment ID. + return webViewFragmentId; + } + + + // Domain settings. + public void setDomainSettingsApplied(boolean applied) { + // Store the domain settings applied status. + domainSettingsApplied = applied; + } + + public boolean getDomainSettingsApplied() { + // Return the domain settings applied status. + return domainSettingsApplied; + } + + + // Domain settings database ID. + public void setDomainSettingsDatabaseId(int databaseId) { + // Store the domain settings database ID. + domainSettingsDatabaseId = databaseId; + } + + public int getDomainSettingsDatabaseId() { + // Return the domain settings database ID. + return domainSettingsDatabaseId; + } + + + // 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. + currentDomainName = domainName; + } + + public void resetCurrentDomainName() { + // Reset the current domain name. + currentDomainName = ""; + } + + public String getCurrentDomainName() { + // Return the current domain name. + return currentDomainName; + } + + + // 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. + return resourceRequests; + } + + public void clearResourceRequests() { + // Clear the resource requests. + resourceRequests.clear(); + } + + + // Blocklists. + public void enableBlocklist(int blocklist, boolean status) { + // Update the status of the indicated blocklist. + switch (blocklist) { + case EASY_LIST: + // Update the status of the blocklist. + easyListEnabled = status; + break; + + case EASY_PRIVACY: + // Update the status of the blocklist. + easyPrivacyEnabled = status; + break; + + case FANBOYS_ANNOYANCE_LIST: + // Update the status of the blocklist. + fanboysAnnoyanceListEnabled = status; + break; + + case FANBOYS_SOCIAL_BLOCKING_LIST: + // Update the status of the blocklist. + fanboysSocialBlockingListEnabled = status; + break; + + case ULTRA_PRIVACY: + // Update the status of the blocklist. + ultraPrivacyEnabled = status; + break; + + case THIRD_PARTY_REQUESTS: + // Update the status of the blocklist. + blockAllThirdPartyRequests = status; + break; + } + } + + public boolean isBlocklistEnabled(int blocklist) { + // Get the status of the indicated blocklist. + switch (blocklist) { + case EASY_LIST: + // Return the status of the blocklist. + return easyListEnabled; + + case EASY_PRIVACY: + // Return the status of the blocklist. + return easyPrivacyEnabled; + + case FANBOYS_ANNOYANCE_LIST: + // Return the status of the blocklist. + return fanboysAnnoyanceListEnabled; + + case FANBOYS_SOCIAL_BLOCKING_LIST: + // Return the status of the blocklist. + return fanboysSocialBlockingListEnabled; + + case ULTRA_PRIVACY: + // Return the status of the blocklist. + return ultraPrivacyEnabled; + + case THIRD_PARTY_REQUESTS: + // Return the status of the blocklist. + return blockAllThirdPartyRequests; + + default: + // The default value is required but should never be used. + return false; + } + } + + + // Resource request counters. + public void resetRequestsCounters() { + // Reset all the resource request counters. + blockedRequests = 0; + easyListBlockedRequests = 0; + easyPrivacyBlockedRequests = 0; + fanboysAnnoyanceListBlockedRequests = 0; + fanboysSocialBlockingListBlockedRequests = 0; + ultraPrivacyBlockedRequests = 0; + thirdPartyBlockedRequests = 0; + } + + public void incrementRequestsCount(int blocklist) { + // Increment the count of the indicated blocklist. + switch (blocklist) { + case BLOCKED_REQUESTS: + // Increment the blocked requests count. + blockedRequests++; + break; + + case EASY_LIST: + // Increment the EasyList blocked requests count. + easyListBlockedRequests++; + break; + + case EASY_PRIVACY: + // Increment the EasyPrivacy blocked requests count. + easyPrivacyBlockedRequests++; + break; + + case FANBOYS_ANNOYANCE_LIST: + // Increment the Fanboy's Annoyance List blocked requests count. + fanboysAnnoyanceListBlockedRequests++; + break; + + case FANBOYS_SOCIAL_BLOCKING_LIST: + // Increment the Fanboy's Social Blocking List blocked requests count. + fanboysSocialBlockingListBlockedRequests++; + break; + + case ULTRA_PRIVACY: + // Increment the UltraPrivacy blocked requests count. + ultraPrivacyBlockedRequests++; + break; + + case THIRD_PARTY_REQUESTS: + // Increment the Third Party blocked requests count. + thirdPartyBlockedRequests++; + break; + } + } + + public int getRequestsCount(int blocklist) { + // Get the count of the indicated blocklist. + switch (blocklist) { + case BLOCKED_REQUESTS: + // Return the blocked requests count. + return blockedRequests; + + case EASY_LIST: + // Return the EasyList blocked requests count. + return easyListBlockedRequests; + + case EASY_PRIVACY: + // Return the EasyPrivacy blocked requests count. + return easyPrivacyBlockedRequests; + + case FANBOYS_ANNOYANCE_LIST: + // Return the Fanboy's Annoyance List blocked requests count. + return fanboysAnnoyanceListBlockedRequests; + + case FANBOYS_SOCIAL_BLOCKING_LIST: + // Return the Fanboy's Social Blocking List blocked requests count. + return fanboysSocialBlockingListBlockedRequests; + + case ULTRA_PRIVACY: + // Return the UltraPrivacy blocked requests count. + return ultraPrivacyBlockedRequests; + + case THIRD_PARTY_REQUESTS: + // Return the Third Party blocked requests count. + return thirdPartyBlockedRequests; + + default: + // Return 0. This should never end up being called. + return 0; + } + } + + + // Pinned SSL certificates. + public boolean hasPinnedSslCertificate() { + // Return the status of the pinned SSL certificate. + return hasPinnedSslCertificate; + } + + public void setPinnedSslCertificate(String issuedToCName, String issuedToOName, String issuedToUName, String issuedByCName, String issuedByOName, String issuedByUName, Date startDate, Date endDate) { + // Store the pinned SSL certificate information. + pinnedSslIssuedToCName = issuedToCName; + pinnedSslIssuedToOName = issuedToOName; + pinnedSslIssuedToUName = issuedToUName; + pinnedSslIssuedByCName = issuedByCName; + pinnedSslIssuedByOName = issuedByOName; + pinnedSslIssuedByUName = issuedByUName; + pinnedSslStartDate = startDate; + pinnedSslEndDate = endDate; + + // Set the pinned SSL certificate tracker. + hasPinnedSslCertificate = true; + } + + public ArrayList getPinnedSslCertificate() { + // Initialize an array list. + ArrayList arrayList = new ArrayList<>(); + + // Create the SSL certificate string array. + String[] sslCertificateStringArray = new String[] {pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName}; + + // Create the SSL certificate date array. + Date[] sslCertificateDateArray = new Date[] {pinnedSslStartDate, pinnedSslEndDate}; + + // Add the arrays to the array list. + arrayList.add(sslCertificateStringArray); + arrayList.add(sslCertificateDateArray); + + // Return the pinned SSL certificate array list. + return arrayList; + } + + public void clearPinnedSslCertificate() { + // Clear the pinned SSL certificate. + pinnedSslIssuedToCName = null; + pinnedSslIssuedToOName = null; + pinnedSslIssuedToUName = null; + pinnedSslIssuedByCName = null; + pinnedSslIssuedByOName = null; + pinnedSslIssuedByUName = null; + pinnedSslStartDate = null; + pinnedSslEndDate = null; + + // Clear the pinned SSL certificate tracker. + hasPinnedSslCertificate = false; + } + + + // Current IP addresses. + public boolean hasCurrentIpAddresses() { + // Return the status of the current IP addresses. + return hasCurrentIpAddresses; + } + + public void setCurrentIpAddresses(String ipAddresses) { + // Store the current IP addresses. + currentIpAddresses = ipAddresses; + + // Set the current IP addresses tracker. + hasCurrentIpAddresses = true; + } + + public String getCurrentIpAddresses() { + // Return the current IP addresses. + return currentIpAddresses; + } + + public void clearCurrentIpAddresses() { + // Clear the current IP addresses. + currentIpAddresses = null; + + // Clear the current IP addresses tracker. + hasCurrentIpAddresses = false; + } + + + // Pinned IP addresses. + public boolean hasPinnedIpAddresses() { + // Return the status of the pinned IP addresses. + return hasPinnedIpAddresses; + } + + public void setPinnedIpAddresses(String ipAddresses) { + // Store the pinned IP addresses. + pinnedIpAddresses = ipAddresses; + + // Set the pinned IP addresses tracker. + hasPinnedIpAddresses = true; + } + + public String getPinnedIpAddresses() { + // Return the pinned IP addresses. + return pinnedIpAddresses; + } + + public void clearPinnedIpAddresses() { + // Clear the pinned IP addresses. + pinnedIpAddresses = null; + + // Clear the pinned IP addresses tracker. + hasPinnedIpAddresses = false; + } + + + // Ignore pinned information. 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; + } + + + @Override public boolean onTouchEvent(MotionEvent motionEvent) { // Initialize a tracker to return if this motion event is handled. @@ -82,19 +500,43 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild case MotionEvent.ACTION_MOVE: // Get the current Y position. - int currentYPosition = (int) motionEvent.getY(); + int currentYMotionPosition = (int) motionEvent.getY(); + + // Calculate the pre-scroll delta Y. + int preScrollDeltaY = previousYPosition - currentYMotionPosition; - // Calculate the delta Y. - int deltaY = previousYPosition - currentYPosition; + // Initialize a variable to track how much of the scroll is consumed. + int[] consumedScroll = new int[2]; - // Store the current Y position for use in the next action move. - previousYPosition = currentYPosition; + // Initialize a variable to track the offset in the window. + int[] offsetInWindow = new int[2]; - // Dispatch the nested pre-school. - dispatchNestedPreScroll(0, deltaY, null, null); + // Get the WebView Y position. + int webViewYPosition = getScrollY(); - // Dispatch the nested scroll. - dispatchNestedScroll(0, deltaY, 0, 0, null); + // Set the scroll delta Y to initially be the same as the pre-scroll delta Y. + int scrollDeltaY = preScrollDeltaY; + + // 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. + 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. + stopNestedScroll(); + } else { // Swipe to refresh is not being engaged. + // Start the nested scroll so that the app bar can scroll off the screen. + startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL); + + // 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. + dispatchNestedScroll(0, scrollDeltaY, 0, 0, offsetInWindow); + + // Store the current Y position for use in the next action move. + previousYPosition = previousYPosition - scrollDeltaY; + } // Run the default commands. motionEventHandled = super.onTouchEvent(motionEvent); @@ -109,10 +551,19 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild motionEventHandled = super.onTouchEvent(motionEvent); } + // Perform a click. This is required by the Android accessibility guidelines. + performClick(); + // Return the status of the motion event. return motionEventHandled; } + // The Android accessibility guidelines require overriding `performClick()` and calling it from `onTouchEvent()`. + @Override + public boolean performClick() { + return super.performClick(); + } + // Method from NestedScrollingChild. @Override