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=f877745580097b4054484118f16db0eb54859ca3;hp=c4f54de9ee2488dc985a6ecdb0ba9fa2b599ccb1;hb=3f3b7c8fbe988fe730a5fbb53169489566655595;hpb=3d167d1ec7d0cef1ef032f20859bb0de8ddb01cf 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 c4f54de9..f8777455 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java +++ b/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java @@ -20,60 +20,133 @@ package com.stoutner.privacybrowser.views; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.MotionEvent; +import android.webkit.HttpAuthHandler; +import android.webkit.SslErrorHandler; import android.webkit.WebView; +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. public final static int BLOCKED_REQUESTS = 0; - public final static int EASY_LIST_BLOCKED_REQUESTS = 1; - public final static int EASY_PRIVACY_BLOCKED_REQUESTS = 2; - public final static int FANBOYS_ANNOYANCE_LIST_BLOCKED_REQUESTS = 3; - public final static int FANBOYS_SOCIAL_BLOCKING_LIST_BLOCKED_REQUESTS = 4; - public final static int ULTRA_PRIVACY_BLOCKED_REQUESTS = 5; - public final static int THIRD_PARTY_BLOCKED_REQUESTS = 6; + 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 ULTRALIST = 5; + public final static int ULTRAPRIVACY = 6; + public final static int THIRD_PARTY_REQUESTS = 7; - // The nested scrolling child helper is used throughout the class. - private NestedScrollingChildHelper nestedScrollingChildHelper; + // Keep a copy of the WebView fragment ID. + private long webViewFragmentId; - // The previous Y position needs to be tracked between motion events. - private int previousYPosition; + // 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. + private boolean acceptFirstPartyCookies; + + // Track the domain settings JavaScript status. This can be removed once night mode does not require JavaScript. + private boolean domainSettingsJavaScriptEnabled; + // 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; private int easyListBlockedRequests; private int easyPrivacyBlockedRequests; private int fanboysAnnoyanceListBlockedRequests; private int fanboysSocialBlockingListBlockedRequests; + private int ultraListBlockedRequests; private int ultraPrivacyBlockedRequests; private int thirdPartyBlockedRequests; - // Basic constructor. + // 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 default or favorite icon. + private Bitmap favoriteOrDefaultIcon; + + // Track night mode. + private boolean nightMode; + + // Track swipe to refresh. + private boolean swipeToRefresh; + + // The nested scrolling child helper is used throughout the class. + private NestedScrollingChildHelper nestedScrollingChildHelper; + + // The previous Y position needs to be tracked between motion events. + private int previousYPosition; + + + + // 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); @@ -85,6 +158,55 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild nestedScrollingChildHelper.setNestedScrollingEnabled(true); } + + + // 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; + } + + + // 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. domainSettingsApplied = applied; @@ -95,6 +217,8 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild return domainSettingsApplied; } + + // Domain settings database ID. public void setDomainSettingsDatabaseId(int databaseId) { // Store the domain settings database ID. domainSettingsDatabaseId = databaseId; @@ -105,13 +229,68 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild return domainSettingsDatabaseId; } + + // 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. + currentDomainName = domainName; + } + + public void resetCurrentDomainName() { + // Reset the current domain name. + currentDomainName = ""; + } + + public String getCurrentDomainName() { + // Return the current domain name. + return currentDomainName; + } + + + // 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; + } + + + // Domain settings JavaScript enabled. This can be removed once night mode does not require JavaScript. + public void setDomainSettingsJavaScriptEnabled(boolean status) { + // Store the domain settings JavaScript status. + domainSettingsJavaScriptEnabled = status; + } + + public boolean getDomainSettingsJavaScriptEnabled() { + // Return the domain settings JavaScript status. + return domainSettingsJavaScriptEnabled; + } + + + // 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; } @@ -120,114 +299,176 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild resourceRequests.clear(); } - public void resetRequestsCount(int list) { - // Run the command on the indicated list. - switch (list) { - case BLOCKED_REQUESTS: - // Reset the blocked requests count. - blockedRequests = 0; + + // Blocklists. + public void enableBlocklist(int blocklist, boolean status) { + // Update the status of the indicated blocklist. + switch (blocklist) { + case EASYLIST: + // Update the status of the blocklist. + easyListEnabled = status; break; - case EASY_LIST_BLOCKED_REQUESTS: - // Reset the EasyList blocked requests count. - easyListBlockedRequests = 0; + case EASYPRIVACY: + // Update the status of the blocklist. + easyPrivacyEnabled = status; break; - case EASY_PRIVACY_BLOCKED_REQUESTS: - // Reset the EasyPrivacy blocked requests count. - easyPrivacyBlockedRequests = 0; + case FANBOYS_ANNOYANCE_LIST: + // Update the status of the blocklist. + fanboysAnnoyanceListEnabled = status; break; - case FANBOYS_ANNOYANCE_LIST_BLOCKED_REQUESTS: - // Reset the Fanboy's Annoyance List blocked requests count. - fanboysAnnoyanceListBlockedRequests = 0; + case FANBOYS_SOCIAL_BLOCKING_LIST: + // Update the status of the blocklist. + fanboysSocialBlockingListEnabled = status; break; - case FANBOYS_SOCIAL_BLOCKING_LIST_BLOCKED_REQUESTS: - // Reset the Fanboy's Social Blocking List blocked requests count. - fanboysSocialBlockingListBlockedRequests = 0; + case ULTRALIST: + // Update the status of the blocklist. + ultraListEnabled = status; break; - case ULTRA_PRIVACY_BLOCKED_REQUESTS: - // Reset the UltraPrivacy blocked requests count. - ultraPrivacyBlockedRequests = 0; + case ULTRAPRIVACY: + // Update the status of the blocklist. + ultraPrivacyEnabled = status; break; - case THIRD_PARTY_BLOCKED_REQUESTS: - // Reset the Third Party blocked requests count. - thirdPartyBlockedRequests = 0; + case THIRD_PARTY_REQUESTS: + // Update the status of the blocklist. + blockAllThirdPartyRequests = status; break; } } - public void incrementRequestsCount(int list) { - // Run the command on the indicated list. - switch (list) { + public boolean isBlocklistEnabled(int blocklist) { + // Get the status of the indicated blocklist. + switch (blocklist) { + case EASYLIST: + // Return the status of the blocklist. + return easyListEnabled; + + case EASYPRIVACY: + // 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 ULTRALIST: + // Return the status of the blocklist. + return ultraListEnabled; + + case ULTRAPRIVACY: + // 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; + ultraListBlockedRequests = 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_BLOCKED_REQUESTS: + case EASYLIST: // Increment the EasyList blocked requests count. easyListBlockedRequests++; break; - case EASY_PRIVACY_BLOCKED_REQUESTS: + case EASYPRIVACY: // Increment the EasyPrivacy blocked requests count. easyPrivacyBlockedRequests++; break; - case FANBOYS_ANNOYANCE_LIST_BLOCKED_REQUESTS: + case FANBOYS_ANNOYANCE_LIST: // Increment the Fanboy's Annoyance List blocked requests count. fanboysAnnoyanceListBlockedRequests++; break; - case FANBOYS_SOCIAL_BLOCKING_LIST_BLOCKED_REQUESTS: + case FANBOYS_SOCIAL_BLOCKING_LIST: // Increment the Fanboy's Social Blocking List blocked requests count. fanboysSocialBlockingListBlockedRequests++; break; - case ULTRA_PRIVACY_BLOCKED_REQUESTS: + case ULTRALIST: + // Increment the UltraList blocked requests count. + ultraListBlockedRequests++; + break; + + case ULTRAPRIVACY: // Increment the UltraPrivacy blocked requests count. ultraPrivacyBlockedRequests++; break; - case THIRD_PARTY_BLOCKED_REQUESTS: + case THIRD_PARTY_REQUESTS: // Increment the Third Party blocked requests count. thirdPartyBlockedRequests++; break; } } - public int getRequestsCount(int list) { - // Run the command on the indicated list. - switch (list) { + 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_BLOCKED_REQUESTS: + case EASYLIST: // Return the EasyList blocked requests count. return easyListBlockedRequests; - case EASY_PRIVACY_BLOCKED_REQUESTS: + case EASYPRIVACY: // Return the EasyPrivacy blocked requests count. return easyPrivacyBlockedRequests; - case FANBOYS_ANNOYANCE_LIST_BLOCKED_REQUESTS: + case FANBOYS_ANNOYANCE_LIST: // Return the Fanboy's Annoyance List blocked requests count. return fanboysAnnoyanceListBlockedRequests; - case FANBOYS_SOCIAL_BLOCKING_LIST_BLOCKED_REQUESTS: + case FANBOYS_SOCIAL_BLOCKING_LIST: // Return the Fanboy's Social Blocking List blocked requests count. return fanboysSocialBlockingListBlockedRequests; - case ULTRA_PRIVACY_BLOCKED_REQUESTS: + case ULTRALIST: + // Return the UltraList blocked requests count. + return ultraListBlockedRequests; + + case ULTRAPRIVACY: // Return the UltraPrivacy blocked requests count. return ultraPrivacyBlockedRequests; - case THIRD_PARTY_BLOCKED_REQUESTS: + case THIRD_PARTY_REQUESTS: // Return the Third Party blocked requests count. return thirdPartyBlockedRequests; @@ -237,6 +478,200 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild } } + + // 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. + 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; + } + + + // Favorite or default icon. + public void initializeFavoriteIcon() { + // Get the default favorite icon drawable. `ContextCompat` must be used until API >= 21. + Drawable favoriteIconDrawable = ContextCompat.getDrawable(getContext(), R.drawable.world); + + // Cast the favorite icon drawable to a bitmap drawable. + BitmapDrawable favoriteIconBitmapDrawable = (BitmapDrawable) favoriteIconDrawable; + + // Remove the incorrect warning below that the favorite icon bitmap drawable might be null. + assert favoriteIconBitmapDrawable != null; + + // Store the default icon bitmap. + favoriteOrDefaultIcon = favoriteIconBitmapDrawable.getBitmap(); + } + + public void setFavoriteOrDefaultIcon(Bitmap icon) { + // Scale the favorite icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation. + if ((icon.getHeight() > 256) || (icon.getWidth() > 256)) { + favoriteOrDefaultIcon = Bitmap.createScaledBitmap(icon, 256, 256, true); + } else { + // Store the icon as presented. + favoriteOrDefaultIcon = icon; + } + } + + public Bitmap getFavoriteOrDefaultIcon() { + // Return the favorite or default icon. + return favoriteOrDefaultIcon; + } + + + // Night mode. + public void setNightMode(boolean status) { + // Store the night mode status. + nightMode = status; + } + + public boolean getNightMode() { + // Return the night mode status. + return nightMode; + } + + + // 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; + } + + + // 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) { // Initialize a tracker to return if this motion event is handled. @@ -277,12 +712,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.