2 * Copyright © 2019 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.util.AttributeSet;
24 import android.view.MotionEvent;
25 import android.webkit.WebView;
27 import androidx.core.view.NestedScrollingChild2;
28 import androidx.core.view.NestedScrollingChildHelper;
29 import androidx.core.view.ViewCompat;
31 import java.util.ArrayList;
33 // NestedScrollWebView extends WebView to handle nested scrolls (scrolling the app bar off the screen).
34 public class NestedScrollWebView extends WebView implements NestedScrollingChild2 {
35 // These constants identify the blocklists.
36 public final static int BLOCKED_REQUESTS = 0;
37 public final static int EASY_LIST_BLOCKED_REQUESTS = 1;
38 public final static int EASY_PRIVACY_BLOCKED_REQUESTS = 2;
39 public final static int FANBOYS_ANNOYANCE_LIST_BLOCKED_REQUESTS = 3;
40 public final static int FANBOYS_SOCIAL_BLOCKING_LIST_BLOCKED_REQUESTS = 4;
41 public final static int ULTRA_PRIVACY_BLOCKED_REQUESTS = 5;
42 public final static int THIRD_PARTY_BLOCKED_REQUESTS = 6;
44 // The nested scrolling child helper is used throughout the class.
45 private NestedScrollingChildHelper nestedScrollingChildHelper;
47 // The previous Y position needs to be tracked between motion events.
48 private int previousYPosition;
50 // Track if domain settings are applied to this nested scroll WebView and, if so, the database ID.
51 private boolean domainSettingsApplied;
52 private int domainSettingsDatabaseId;
54 // Track the resource requests.
55 private ArrayList<String[]> resourceRequests = new ArrayList<>();
56 private int blockedRequests;
57 private int easyListBlockedRequests;
58 private int easyPrivacyBlockedRequests;
59 private int fanboysAnnoyanceListBlockedRequests;
60 private int fanboysSocialBlockingListBlockedRequests;
61 private int ultraPrivacyBlockedRequests;
62 private int thirdPartyBlockedRequests;
65 public NestedScrollWebView(Context context) {
66 // Roll up to the next constructor.
70 // Intermediate constructor.
71 public NestedScrollWebView(Context context, AttributeSet attributeSet) {
72 // Roll up to the next constructor.
73 this(context, attributeSet, android.R.attr.webViewStyle);
77 public NestedScrollWebView(Context context, AttributeSet attributeSet, int defaultStyle) {
78 // Run the default commands.
79 super(context, attributeSet, defaultStyle);
81 // Initialize the nested scrolling child helper.
82 nestedScrollingChildHelper = new NestedScrollingChildHelper(this);
84 // Enable nested scrolling by default.
85 nestedScrollingChildHelper.setNestedScrollingEnabled(true);
88 public void setDomainSettingsApplied(boolean applied) {
89 // Store the domain settings applied status.
90 domainSettingsApplied = applied;
93 public boolean getDomainSettingsApplied() {
94 // Return the domain settings applied status.
95 return domainSettingsApplied;
98 public void setDomainSettingsDatabaseId(int databaseId) {
99 // Store the domain settings database ID.
100 domainSettingsDatabaseId = databaseId;
103 public int getDomainSettingsDatabaseId() {
104 // Return the domain settings database ID.
105 return domainSettingsDatabaseId;
108 public void addResourceRequest(String[] resourceRequest) {
109 // Add the resource request to the list.
110 resourceRequests.add(resourceRequest);
113 public ArrayList<String[]> getResourceRequests() {
114 // Return the list of resource requests.
115 return resourceRequests;
118 public void clearResourceRequests() {
119 // Clear the resource requests.
120 resourceRequests.clear();
123 public void resetRequestsCount(int list) {
124 // Run the command on the indicated list.
126 case BLOCKED_REQUESTS:
127 // Reset the blocked requests count.
131 case EASY_LIST_BLOCKED_REQUESTS:
132 // Reset the EasyList blocked requests count.
133 easyListBlockedRequests = 0;
136 case EASY_PRIVACY_BLOCKED_REQUESTS:
137 // Reset the EasyPrivacy blocked requests count.
138 easyPrivacyBlockedRequests = 0;
141 case FANBOYS_ANNOYANCE_LIST_BLOCKED_REQUESTS:
142 // Reset the Fanboy's Annoyance List blocked requests count.
143 fanboysAnnoyanceListBlockedRequests = 0;
146 case FANBOYS_SOCIAL_BLOCKING_LIST_BLOCKED_REQUESTS:
147 // Reset the Fanboy's Social Blocking List blocked requests count.
148 fanboysSocialBlockingListBlockedRequests = 0;
151 case ULTRA_PRIVACY_BLOCKED_REQUESTS:
152 // Reset the UltraPrivacy blocked requests count.
153 ultraPrivacyBlockedRequests = 0;
156 case THIRD_PARTY_BLOCKED_REQUESTS:
157 // Reset the Third Party blocked requests count.
158 thirdPartyBlockedRequests = 0;
163 public void incrementRequestsCount(int list) {
164 // Run the command on the indicated list.
166 case BLOCKED_REQUESTS:
167 // Increment the blocked requests count.
171 case EASY_LIST_BLOCKED_REQUESTS:
172 // Increment the EasyList blocked requests count.
173 easyListBlockedRequests++;
176 case EASY_PRIVACY_BLOCKED_REQUESTS:
177 // Increment the EasyPrivacy blocked requests count.
178 easyPrivacyBlockedRequests++;
181 case FANBOYS_ANNOYANCE_LIST_BLOCKED_REQUESTS:
182 // Increment the Fanboy's Annoyance List blocked requests count.
183 fanboysAnnoyanceListBlockedRequests++;
186 case FANBOYS_SOCIAL_BLOCKING_LIST_BLOCKED_REQUESTS:
187 // Increment the Fanboy's Social Blocking List blocked requests count.
188 fanboysSocialBlockingListBlockedRequests++;
191 case ULTRA_PRIVACY_BLOCKED_REQUESTS:
192 // Increment the UltraPrivacy blocked requests count.
193 ultraPrivacyBlockedRequests++;
196 case THIRD_PARTY_BLOCKED_REQUESTS:
197 // Increment the Third Party blocked requests count.
198 thirdPartyBlockedRequests++;
203 public int getRequestsCount(int list) {
204 // Run the command on the indicated list.
206 case BLOCKED_REQUESTS:
207 // Return the blocked requests count.
208 return blockedRequests;
210 case EASY_LIST_BLOCKED_REQUESTS:
211 // Return the EasyList blocked requests count.
212 return easyListBlockedRequests;
214 case EASY_PRIVACY_BLOCKED_REQUESTS:
215 // Return the EasyPrivacy blocked requests count.
216 return easyPrivacyBlockedRequests;
218 case FANBOYS_ANNOYANCE_LIST_BLOCKED_REQUESTS:
219 // Return the Fanboy's Annoyance List blocked requests count.
220 return fanboysAnnoyanceListBlockedRequests;
222 case FANBOYS_SOCIAL_BLOCKING_LIST_BLOCKED_REQUESTS:
223 // Return the Fanboy's Social Blocking List blocked requests count.
224 return fanboysSocialBlockingListBlockedRequests;
226 case ULTRA_PRIVACY_BLOCKED_REQUESTS:
227 // Return the UltraPrivacy blocked requests count.
228 return ultraPrivacyBlockedRequests;
230 case THIRD_PARTY_BLOCKED_REQUESTS:
231 // Return the Third Party blocked requests count.
232 return thirdPartyBlockedRequests;
235 // Return 0. This should never end up being called.
241 public boolean onTouchEvent(MotionEvent motionEvent) {
242 // Initialize a tracker to return if this motion event is handled.
243 boolean motionEventHandled;
245 // Run the commands for the given motion event action.
246 switch (motionEvent.getAction()) {
247 case MotionEvent.ACTION_DOWN:
248 // Start nested scrolling along the vertical axis. `ViewCompat` must be used until the minimum API >= 21.
249 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
251 // Save the current Y position. Action down will not be called again until a new motion starts.
252 previousYPosition = (int) motionEvent.getY();
254 // Run the default commands.
255 motionEventHandled = super.onTouchEvent(motionEvent);
258 case MotionEvent.ACTION_MOVE:
259 // Get the current Y position.
260 int currentYMotionPosition = (int) motionEvent.getY();
262 // Calculate the pre-scroll delta Y.
263 int preScrollDeltaY = previousYPosition - currentYMotionPosition;
265 // Initialize a variable to track how much of the scroll is consumed.
266 int[] consumedScroll = new int[2];
268 // Initialize a variable to track the offset in the window.
269 int[] offsetInWindow = new int[2];
271 // Get the WebView Y position.
272 int webViewYPosition = getScrollY();
274 // Set the scroll delta Y to initially be the same as the pre-scroll delta Y.
275 int scrollDeltaY = preScrollDeltaY;
277 // Dispatch the nested pre-school. This scrolls the app bar if it needs it. `offsetInWindow` will be returned with an updated value.
278 if (dispatchNestedPreScroll(0, preScrollDeltaY, consumedScroll, offsetInWindow)) {
279 // Update the scroll delta Y if some of it was consumed.
280 scrollDeltaY = preScrollDeltaY - consumedScroll[1];
283 // Check to see if the WebView is at the top and and the scroll action is downward.
284 if ((webViewYPosition == 0) && (scrollDeltaY < 0)) { // Swipe to refresh is being engaged.
285 // Stop the nested scroll so that swipe to refresh has complete control.
287 } else { // Swipe to refresh is not being engaged.
288 // Start the nested scroll so that the app bar can scroll off the screen.
289 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
291 // 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.
292 dispatchNestedScroll(0, scrollDeltaY, 0, 0, offsetInWindow);
294 // Store the current Y position for use in the next action move.
295 previousYPosition = previousYPosition - scrollDeltaY;
298 // Run the default commands.
299 motionEventHandled = super.onTouchEvent(motionEvent);
304 // Stop nested scrolling.
307 // Run the default commands.
308 motionEventHandled = super.onTouchEvent(motionEvent);
311 // Perform a click. This is required by the Android accessibility guidelines.
314 // Return the status of the motion event.
315 return motionEventHandled;
318 // The Android accessibility guidelines require overriding `performClick()` and calling it from `onTouchEvent()`.
320 public boolean performClick() {
321 return super.performClick();
325 // Method from NestedScrollingChild.
327 public void setNestedScrollingEnabled(boolean status) {
328 // Set the status of the nested scrolling.
329 nestedScrollingChildHelper.setNestedScrollingEnabled(status);
332 // Method from NestedScrollingChild.
334 public boolean isNestedScrollingEnabled() {
335 // Return the status of nested scrolling.
336 return nestedScrollingChildHelper.isNestedScrollingEnabled();
340 // Method from NestedScrollingChild.
342 public boolean startNestedScroll(int axes) {
343 // Start a nested scroll along the indicated axes.
344 return nestedScrollingChildHelper.startNestedScroll(axes);
347 // Method from NestedScrollingChild2.
349 public boolean startNestedScroll(int axes, int type) {
350 // Start a nested scroll along the indicated axes for the given type of input which caused the scroll event.
351 return nestedScrollingChildHelper.startNestedScroll(axes, type);
355 // Method from NestedScrollingChild.
357 public void stopNestedScroll() {
358 // Stop the nested scroll.
359 nestedScrollingChildHelper.stopNestedScroll();
362 // Method from NestedScrollingChild2.
364 public void stopNestedScroll(int type) {
365 // Stop the nested scroll of the given type of input which caused the scroll event.
366 nestedScrollingChildHelper.stopNestedScroll(type);
370 // Method from NestedScrollingChild.
372 public boolean hasNestedScrollingParent() {
373 // Return the status of the nested scrolling parent.
374 return nestedScrollingChildHelper.hasNestedScrollingParent();
377 // Method from NestedScrollingChild2.
379 public boolean hasNestedScrollingParent(int type) {
380 // return the status of the nested scrolling parent for the given type of input which caused the scroll event.
381 return nestedScrollingChildHelper.hasNestedScrollingParent(type);
385 // Method from NestedScrollingChild.
387 public boolean dispatchNestedPreScroll(int deltaX, int deltaY, int[] consumed, int[] offsetInWindow) {
388 // Dispatch a nested pre-scroll with the specified deltas, which lets a parent to consume some of the scroll if desired.
389 return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow);
392 // Method from NestedScrollingChild2.
394 public boolean dispatchNestedPreScroll(int deltaX, int deltaY, int[] consumed, int[] offsetInWindow, int type) {
395 // 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.
396 return nestedScrollingChildHelper.dispatchNestedPreScroll(deltaX, deltaY, consumed, offsetInWindow, type);
400 // Method from NestedScrollingChild.
402 public boolean dispatchNestedScroll(int deltaXConsumed, int deltaYConsumed, int deltaXUnconsumed, int deltaYUnconsumed, int[] offsetInWindow) {
403 // Dispatch a nested scroll with the specified deltas.
404 return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow);
407 // Method from NestedScrollingChild2.
409 public boolean dispatchNestedScroll(int deltaXConsumed, int deltaYConsumed, int deltaXUnconsumed, int deltaYUnconsumed, int[] offsetInWindow, int type) {
410 // Dispatch a nested scroll with the specified deltas for the given type of input which caused the scroll event.
411 return nestedScrollingChildHelper.dispatchNestedScroll(deltaXConsumed, deltaYConsumed, deltaXUnconsumed, deltaYUnconsumed, offsetInWindow, type);
415 // Method from NestedScrollingChild.
417 public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
418 // Dispatch a nested pre-fling with the specified velocity, which lets a parent consume the fling if desired.
419 return nestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
422 // Method from NestedScrollingChild.
424 public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
425 // Dispatch a nested fling with the specified velocity.
426 return nestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);