X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FMainWebViewActivity.java;h=1a8ba7d5b53291ba57c2a4876d561627a22d44fc;hp=45a1de5b1c94e2c9267c6355eeaa8e9ccf113622;hb=e2d4437956f57b50bda1f27c9b4eea9367de7758;hpb=1af1a793790badfcaf5497c82c4e7b70c7fdb69e diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java index 45a1de5b..1a8ba7d5 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -118,7 +118,6 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.viewpager.widget.ViewPager; import androidx.webkit.WebSettingsCompat; import androidx.webkit.WebViewFeature; -import kotlin.Pair; import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.floatingactionbutton.FloatingActionButton; @@ -153,6 +152,7 @@ import com.stoutner.privacybrowser.helpers.BlocklistHelper; import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper; import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper; import com.stoutner.privacybrowser.helpers.ProxyHelper; +import com.stoutner.privacybrowser.helpers.SanitizeUrlHelper; import com.stoutner.privacybrowser.views.NestedScrollWebView; import java.io.ByteArrayInputStream; @@ -174,15 +174,15 @@ import java.text.NumberFormat; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import kotlin.Pair; + public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, OpenDialog.OpenListener, PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveDialog.SaveListener, UrlHistoryDialog.NavigateHistoryListener, @@ -195,7 +195,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook public static String proxyMode = ProxyHelper.NONE; // Declare the public static variables. - public static String currentBookmarksFolder; + public static String currentBookmarksFolder = ""; public static boolean restartFromBookmarksActivity; public static WebViewPagerAdapter webViewPagerAdapter; @@ -234,9 +234,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxy()`, and `applyDomainSettings()`. private NestedScrollWebView currentWebView; - // `customHeader` is used in `onCreate()`, `onOptionsItemSelected()`, `onCreateContextMenu()`, and `loadUrl()`. - private final Map customHeaders = new HashMap<>(); - // The search URL is set in `applyAppSettings()` and used in `onNewIntent()`, `loadUrlFromTextBox()`, `initializeApp()`, and `initializeWebView()`. private String searchURL; @@ -273,13 +270,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private int defaultProgressViewStartOffset; private int defaultProgressViewEndOffset; - // The URL sanitizers are set in `applyAppSettings()` and used in `sanitizeUrl()`. - private boolean sanitizeGoogleAnalytics; - private boolean sanitizeFacebookClickIds; - private boolean sanitizeTwitterAmpRedirects; + // Declare the helpers. + private BookmarksDatabaseHelper bookmarksDatabaseHelper; + private DomainsDatabaseHelper domainsDatabaseHelper; + private ProxyHelper proxyHelper; + private SanitizeUrlHelper sanitizeUrlHelper; // Declare the class variables - private BookmarksDatabaseHelper bookmarksDatabaseHelper; private boolean bottomAppBar; private boolean displayingFullScreenVideo; private boolean downloadWithExternalApp; @@ -289,9 +286,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private boolean inFullScreenBrowsingMode; private boolean loadingNewIntent; private BroadcastReceiver orbotStatusBroadcastReceiver; - private ProxyHelper proxyHelper; private boolean reapplyAppSettingsOnRestart; private boolean reapplyDomainSettingsOnRestart; + private boolean sanitizeAmpRedirects; + private boolean sanitizeTrackingQueries; private boolean scrollAppBar; private boolean waitingForProxy; private String webViewDefaultUserAgent; @@ -532,15 +530,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Enable the drawing of the entire webpage. This makes it possible to save a website image. This must be done before anything else happens with the WebView. WebView.enableSlowWholeDocumentDraw(); - // Set the theme. - setTheme(R.style.PrivacyBrowser); - - // Set the content view. - if (bottomAppBar) { - setContentView(R.layout.main_framelayout_bottom_appbar); - } else { - setContentView(R.layout.main_framelayout_top_appbar); - } + // Set the content view according to the position of the app bar. + if (bottomAppBar) setContentView(R.layout.main_framelayout_bottom_appbar); + else setContentView(R.layout.main_framelayout_top_appbar); // Get handles for the views. rootFrameLayout = findViewById(R.id.root_framelayout); @@ -603,8 +595,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Store up to 100 tabs in memory. webViewPager.setOffscreenPageLimit(100); - // Instantiate the proxy helper. + // Instantiate the helpers. + bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this); + domainsDatabaseHelper = new DomainsDatabaseHelper(this); proxyHelper = new ProxyHelper(); + sanitizeUrlHelper = new SanitizeUrlHelper(); // Initialize the app. initializeApp(); @@ -621,9 +616,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Run the default commands. super.onNewIntent(intent); - // Replace the intent that started the app with this one. - setIntent(intent); - // Check to see if the app is being restarted from a saved state. if (savedStateArrayList == null || savedStateArrayList.size() == 0) { // The activity is not being restarted from a saved state. // Get the information from the intent. @@ -695,6 +687,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook drawerLayout.closeDrawer(GravityCompat.END); } } + } else { // The app has been restarted. + // Replace the intent that started the app with this one. This will load the tab after the others have been restored. + setIntent(intent); } } @@ -1955,9 +1950,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook Uri currentUri = Uri.parse(currentWebView.getUrl()); String currentDomain = currentUri.getHost(); - // Initialize the database handler. - DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this); - // Create the domain and store the database ID. int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain); @@ -3385,15 +3377,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook drawerLayout.setDrawerTitle(GravityCompat.START, getString(R.string.navigation_drawer)); drawerLayout.setDrawerTitle(GravityCompat.END, getString(R.string.bookmarks)); - // Initialize the bookmarks database helper. - bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this); - - // Initialize `currentBookmarksFolder`. `""` is the home folder in the database. - currentBookmarksFolder = ""; - - // Load the home folder, which is `""` in the database. + // Load the bookmarks folder. loadBookmarksFolder(); + // Handle clicks on bookmarks. bookmarksListView.setOnItemClickListener((parent, view, position, id) -> { // Convert the id from long to int to match the format of the bookmarks database. int databaseId = (int) id; @@ -3451,7 +3438,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook addNewTab(bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)), false); // Display a snackbar. - Snackbar.make(currentWebView, R.string.bookmark_opened_in_background, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(drawerLayout, R.string.bookmark_opened_in_background, Snackbar.LENGTH_SHORT).show(); } // Consume the event. @@ -3500,9 +3487,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } }); - // Replace the header that `WebView` creates for `X-Requested-With` with a null value. The default value is the application ID (com.stoutner.privacybrowser.standard). - customHeaders.put("X-Requested-With", ""); - // Inflate a bare WebView to get the default user agent. It is not used to render content on the screen. @SuppressLint("InflateParams") View webViewLayout = getLayoutInflater().inflate(R.layout.bare_webview, null, false); @@ -3522,9 +3506,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Store the values from the shared preferences in variables. incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false); - sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true); - sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true); - sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true); + sanitizeTrackingQueries = sharedPreferences.getBoolean(getString(R.string.tracking_queries_key), true); + sanitizeAmpRedirects = sharedPreferences.getBoolean(getString(R.string.amp_redirects_key), true); proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value)); fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false); downloadWithExternalApp = sharedPreferences.getBoolean(getString(R.string.download_with_external_app_key), false); @@ -3553,8 +3536,26 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Apply the proxy. applyProxy(false); - // Adjust the layout and scrolling parameters if the app bar is at the top of the screen. - if (!bottomAppBar) { + // Adjust the layout and scrolling parameters according to the position of the app bar. + if (bottomAppBar) { // The app bar is on the bottom. + // Adjust the UI. + if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) { // The app bar scrolls or full screen browsing mode is engaged with the app bar hidden. + // Reset the WebView padding to fill the available space. + swipeRefreshLayout.setPadding(0, 0, 0, 0); + } else { // The app bar doesn't scroll or full screen browsing mode is not engaged with the app bar hidden. + // Move the WebView above the app bar layout. + swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight); + + // Show the app bar if it is scrolled off the screen. + if (appBarLayout.getTranslationY() != 0) { + // Animate the bottom app bar onto the screen. + objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", 0); + + // Make it so. + objectAnimator.start(); + } + } + } else { // The app bar is on the top. // Get the current layout parameters. Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command. CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams(); @@ -3722,10 +3723,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - // Initialize the database handler. - DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this); - - // Get a full cursor from `domainsDatabaseHelper`. + // Get a full domain name cursor. Cursor domainNameCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomain(); // Initialize `domainSettingsSet`. @@ -3780,6 +3778,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); // Store the general preference information. + boolean defaultXRequestedWithHeader = sharedPreferences.getBoolean(getString(R.string.x_requested_with_header_key), true); String defaultFontSizeString = sharedPreferences.getString("font_size", getString(R.string.font_size_default_value)); String defaultUserAgentName = sharedPreferences.getString("user_agent", getString(R.string.user_agent_default_value)); boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true); @@ -3816,13 +3815,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook boolean saveFormData = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1); nestedScrollWebView.setEasyListEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1); nestedScrollWebView.setEasyPrivacyEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1); - nestedScrollWebView.setFanboysAnnoyanceListEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1); + nestedScrollWebView.setFanboysAnnoyanceListEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow( + DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1); nestedScrollWebView.setFanboysSocialBlockingListEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow( DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1); nestedScrollWebView.setUltraListEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST)) == 1); nestedScrollWebView.setUltraPrivacyEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)) == 1); - nestedScrollWebView.setBlockAllThirdPartyRequests(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1); + nestedScrollWebView.setBlockAllThirdPartyRequests(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow( + DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1); String userAgentName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT)); + int xRequestedWithHeaderInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.X_REQUESTED_WITH_HEADER)); int fontSize = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE)); int swipeToRefreshInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH)); int webViewThemeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME)); @@ -3862,6 +3864,24 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook nestedScrollWebView.getSettings().setSaveFormData(saveFormData); } + // Set the X-Requested-With header. + switch (xRequestedWithHeaderInt) { + case DomainsDatabaseHelper.SYSTEM_DEFAULT: + if (defaultXRequestedWithHeader) + nestedScrollWebView.setXRequestedWithHeader(); + else + nestedScrollWebView.resetXRequestedWithHeader(); + break; + + case DomainsDatabaseHelper.ENABLED: + nestedScrollWebView.setXRequestedWithHeader(); + break; + + case DomainsDatabaseHelper.DISABLED: + nestedScrollWebView.resetXRequestedWithHeader(); + break; + } + // Apply the font size. try { // Try the specified font size to see if it is valid. if (fontSize == 0) { // Apply the default font size. @@ -3951,6 +3971,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Disable swipe to refresh. swipeRefreshLayout.setEnabled(false); + break; } // Check to see if WebView themes are supported. @@ -4055,6 +4076,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook nestedScrollWebView.getSettings().setSaveFormData(saveFormData); } + // Store the X-Requested-With header status in the nested scroll WebView. + if (defaultXRequestedWithHeader) + nestedScrollWebView.setXRequestedWithHeader(); + else + nestedScrollWebView.resetXRequestedWithHeader(); + // Store the swipe to refresh status in the nested scroll WebView. nestedScrollWebView.setSwipeToRefresh(defaultSwipeToRefresh); @@ -4143,7 +4170,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Load the URL if directed. This makes sure that the domain settings are properly loaded before the URL. By using `loadUrl()`, instead of `loadUrlFromBase()`, the Referer header will never be sent. if (loadUrl) { - nestedScrollWebView.loadUrl(url, customHeaders); + nestedScrollWebView.loadUrl(url, nestedScrollWebView.getXRequestedWithHeader()); } } @@ -4483,49 +4510,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } private String sanitizeUrl(String url) { - // Sanitize Google Analytics. - if (sanitizeGoogleAnalytics) { - // Remove `?utm_`. - if (url.contains("?utm_")) { - url = url.substring(0, url.indexOf("?utm_")); - } - - // Remove `&utm_`. - if (url.contains("&utm_")) { - url = url.substring(0, url.indexOf("&utm_")); - } - } + // Sanitize tracking queries. + if (sanitizeTrackingQueries) + url = sanitizeUrlHelper.sanitizeTrackingQueries(url); - // Sanitize Facebook Click IDs. - if (sanitizeFacebookClickIds) { - // Remove `?fbclid=`. - if (url.contains("?fbclid=")) { - url = url.substring(0, url.indexOf("?fbclid=")); - } - - // Remove `&fbclid=`. - if (url.contains("&fbclid=")) { - url = url.substring(0, url.indexOf("&fbclid=")); - } - - // Remove `?fbadid=`. - if (url.contains("?fbadid=")) { - url = url.substring(0, url.indexOf("?fbadid=")); - } - - // Remove `&fbadid=`. - if (url.contains("&fbadid=")) { - url = url.substring(0, url.indexOf("&fbadid=")); - } - } - - // Sanitize Twitter AMP redirects. - if (sanitizeTwitterAmpRedirects) { - // Remove `?amp=1`. - if (url.contains("?amp=1")) { - url = url.substring(0, url.indexOf("?amp=1")); - } - } + // Sanitize AMP redirects. + if (sanitizeAmpRedirects) + url = sanitizeUrlHelper.sanitizeAmpRedirects(url); // Return the sanitized URL. return url; @@ -4901,9 +4892,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - // Clear the custom headers. - customHeaders.clear(); - // Manually delete the `app_webview` folder, which contains the cookies, DOM storage, form data, and `Service Worker` cache. // See `https://code.google.com/p/android/issues/detail?id=233826&thanks=233826&ts=1486670530`. if (clearEverything) { @@ -5121,7 +5109,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Toggle the full screen browsing mode. if (inFullScreenBrowsingMode) { // Switch to full screen mode. // Hide the app bar if specified. - if (hideAppBar) { + if (hideAppBar) { // The app bar is hidden. // Close the find on page bar if it is visible. closeFindOnPage(null); @@ -5131,8 +5119,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Hide the action bar. actionBar.hide(); - // Set layout and scrolling parameters if the app bar is at the top of the screen. - if (!bottomAppBar) { + // Set layout and scrolling parameters according to the position of the app bar. + if (bottomAppBar) { // The app bar is at the bottom. + // Reset the WebView padding to fill the available space. + swipeRefreshLayout.setPadding(0, 0, 0, 0); + } else { // The app bar is at the top. // Check to see if the app bar is normally scrolled. if (scrollAppBar) { // The app bar is scrolled when it is displayed. // Get the swipe refresh layout parameters. @@ -5148,6 +5139,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook swipeRefreshLayout.setProgressViewOffset(false, -200, defaultProgressViewEndOffset); } } + } else { // The app bar is not hidden. + // Adjust the UI for the bottom app bar. + if (bottomAppBar) { + // Adjust the UI according to the scrolling of the app bar. + if (scrollAppBar) { + // Reset the WebView padding to fill the available space. + swipeRefreshLayout.setPadding(0, 0, 0, 0); + } else { + // Move the WebView above the app bar layout. + swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight); + } + } } /* Hide the system bars. @@ -5166,23 +5169,32 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Show the action bar. actionBar.show(); + } - // Set layout and scrolling parameters if the app bar is at the top of the screen. - if (!bottomAppBar) { - // Check to see if the app bar is normally scrolled. - if (scrollAppBar) { // The app bar is scrolled when it is displayed. - // Get the swipe refresh layout parameters. - CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); - - // Add the off-screen scrolling layout. - swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior()); - } else { // The app bar is not scrolled when it is displayed. - // The swipe refresh layout must be manually moved below the app bar layout. - swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0); - - // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. - swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight); - } + // Set layout and scrolling parameters according to the position of the app bar. + if (bottomAppBar) { // The app bar is at the bottom. + // Adjust the UI. + if (scrollAppBar) { + // Reset the WebView padding to fill the available space. + swipeRefreshLayout.setPadding(0, 0, 0, 0); + } else { + // Move the WebView above the app bar layout. + swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight); + } + } else { // The app bar is at the top. + // Check to see if the app bar is normally scrolled. + if (scrollAppBar) { // The app bar is scrolled when it is displayed. + // Get the swipe refresh layout parameters. + CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); + + // Add the off-screen scrolling layout. + swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior()); + } else { // The app bar is not scrolled when it is displayed. + // The swipe refresh layout must be manually moved below the app bar layout. + swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0); + + // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. + swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight); } } @@ -5196,6 +5208,30 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return false; } } + + @Override + public boolean onFling(MotionEvent motionEvent1, MotionEvent motionEvent2, float velocityX, float velocityY) { + // Scroll the bottom app bar if enabled. + if (bottomAppBar && scrollAppBar && !objectAnimator.isRunning()) { + // Calculate the Y change. + float motionY = motionEvent2.getY() - motionEvent1.getY(); + + // Scroll the app bar if the change is greater than 100 pixels. + if (motionY > 50) { + // Animate the bottom app bar onto the screen. + objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", 0); + } else if (motionY < -50) { + // Animate the bottom app bar off the screen. + objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", appBarLayout.getHeight()); + } + + // Make it so. + objectAnimator.start(); + } + + // Do not consume the event. + return false; + } }); // Pass all touch events on the WebView through the double-tap gesture detector. @@ -5269,7 +5305,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } }); - // Update the status of swipe to refresh based on the scroll position of the nested scroll WebView. Also reinforce full screen browsing mode. + // Process scroll changes. nestedScrollWebView.setOnScrollChangeListener((view, scrollX, scrollY, oldScrollX, oldScrollY) -> { // Set the swipe to refresh status. if (nestedScrollWebView.getSwipeToRefresh()) { @@ -5280,23 +5316,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook swipeRefreshLayout.setEnabled(false); } - // Scroll the bottom app bar if enabled. - if (bottomAppBar && scrollAppBar && !objectAnimator.isRunning()) { - if (scrollY < oldScrollY) { // The WebView was scrolled down. - // Animate the bottom app bar onto the screen. - objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", 0); - - // Make it so. - objectAnimator.start(); - } else if (scrollY > oldScrollY) { // The WebView was scrolled up. - // Animate the bottom app bar off the screen. - objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", appBarLayout.getHeight()); - - // Make it so. - objectAnimator.start(); - } - } - // Reinforce the system UI visibility flags if in full screen browsing mode. // This hides the status and navigation bars, which are displayed if other elements are shown, like dialog boxes, the options menu, or the keyboard. if (inFullScreenBrowsingMode) { @@ -5909,8 +5928,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { - // Set the padding and layout settings if the app bar is at the top of the screen. - if (!bottomAppBar) { + // Get the app bar layout height. This can't be done in `applyAppSettings()` because the app bar is not yet populated there. + // This should only be populated if it is greater than 0 because otherwise it will be reset to 0 if the app bar is hidden in full screen browsing mode. + if (appBarLayout.getHeight() > 0) appBarHeight = appBarLayout.getHeight(); + + // Set the padding and layout settings according to the position of the app bar. + if (bottomAppBar) { // The app bar is on the bottom. + // Adjust the UI. + if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) { // The app bar scrolls or full screen browsing mode is engaged with the app bar hidden. + // Reset the WebView padding to fill the available space. + swipeRefreshLayout.setPadding(0, 0, 0, 0); + } else { // The app bar doesn't scroll or full screen browsing mode is not engaged with the app bar hidden. + // Move the WebView above the app bar layout. + swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight); + } + } else { // The app bar is on the top. // Set the top padding of the swipe refresh layout according to the app bar scrolling preference. This can't be done in `appAppSettings()` because the app bar is not yet populated there. if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) { // No padding is needed because it will automatically be placed below the app bar layout due to the scrolling layout behavior. @@ -5919,9 +5951,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10, defaultProgressViewEndOffset); } else { - // Get the app bar layout height. This can't be done in `applyAppSettings()` because the app bar is not yet populated there. - appBarHeight = appBarLayout.getHeight(); - // The swipe refresh layout must be manually moved below the app bar layout. swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0);