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=13d81cb9f5eb103e3e839de96804f2e408c4bb47;hp=4168f71759530b89c2946489ce5df2ba4fb8aee1;hb=0b2da52fc661cf302cd94105620e54340d269f1d;hpb=ab11ca2de00c56982e46627c8e7fc670462b0b3c 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 4168f717..13d81cb9 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -1,22 +1,22 @@ /* - * Copyright © 2015-2021 Soren Stoutner . + * Copyright © 2015-2022 Soren Stoutner . * * Download cookie code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner . * - * This file is part of Privacy Browser . + * This file is part of Privacy Browser Android . * - * Privacy Browser is free software: you can redistribute it and/or modify + * Privacy Browser Android is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * - * Privacy Browser is distributed in the hope that it will be useful, + * Privacy Browser Android is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with Privacy Browser. If not, see . + * along with Privacy Browser Android. If not, see . */ package com.stoutner.privacybrowser.activities; @@ -152,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; @@ -182,6 +183,8 @@ 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, @@ -204,10 +207,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // The user agent constants are public static so they can be accessed from `SettingsFragment`, `DomainsActivity`, and `DomainSettingsFragment`. public final static int UNRECOGNIZED_USER_AGENT = -1; public final static int SETTINGS_WEBVIEW_DEFAULT_USER_AGENT = 1; - public final static int SETTINGS_CUSTOM_USER_AGENT = 12; + public final static int SETTINGS_CUSTOM_USER_AGENT = 11; public final static int DOMAINS_SYSTEM_DEFAULT_USER_AGENT = 0; public final static int DOMAINS_WEBVIEW_DEFAULT_USER_AGENT = 2; - public final static int DOMAINS_CUSTOM_USER_AGENT = 13; + public final static int DOMAINS_CUSTOM_USER_AGENT = 12; // Define the start activity for result request codes. The public static entry is accessed from `OpenDialog()`. private final int BROWSE_FILE_UPLOAD_REQUEST_CODE = 0; @@ -272,13 +275,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; @@ -288,9 +291,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; @@ -441,7 +445,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook Snackbar.make(currentWebView, getString(R.string.file_saved) + " " + fileNameString, Snackbar.LENGTH_SHORT).show(); } catch (Exception exception) { // Display a snackbar with the exception. - Snackbar.make(currentWebView, getString(R.string.error_saving_file) + " " + exception.toString(), Snackbar.LENGTH_INDEFINITE).show(); + Snackbar.make(currentWebView, getString(R.string.error_saving_file) + " " + exception, Snackbar.LENGTH_INDEFINITE).show(); } finally { // Delete the temporary MHT file. //noinspection ResultOfMethodCallIgnored @@ -454,7 +458,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook }); } catch (IOException ioException) { // Display a snackbar with the IO exception. - Snackbar.make(currentWebView, getString(R.string.error_saving_file) + " " + ioException.toString(), Snackbar.LENGTH_INDEFINITE).show(); + Snackbar.make(currentWebView, getString(R.string.error_saving_file) + " " + ioException, Snackbar.LENGTH_INDEFINITE).show(); } } } @@ -534,12 +538,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); @@ -602,8 +603,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(); @@ -999,17 +1003,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the title. optionsRefreshMenuItem.setTitle(R.string.stop); - // Set the icon if it is displayed in the app bar. + // Set the icon if it is displayed in the app bar. Once the minimum API is >= 26, the blue and black icons can be combined with a tint list. if (displayAdditionalAppBarIcons) { - // Get the current theme status. - int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - - // Set the icon according to the current theme status. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - optionsRefreshMenuItem.setIcon(R.drawable.close_blue_day); - } else { - optionsRefreshMenuItem.setIcon(R.drawable.close_blue_night); - } + optionsRefreshMenuItem.setIcon(R.drawable.close_blue); } } @@ -1857,24 +1853,45 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Consume the event. return true; - } else if (menuItemId == R.id.share_url) { // Share URL. - // Setup the share string. + } else if (menuItemId == R.id.share_message) { // Share a message. + // Prepare the share string. String shareString = currentWebView.getTitle() + " – " + currentWebView.getUrl(); // Create the share intent. - Intent shareIntent = new Intent(Intent.ACTION_SEND); + Intent shareMessageIntent = new Intent(Intent.ACTION_SEND); // Add the share string to the intent. - shareIntent.putExtra(Intent.EXTRA_TEXT, shareString); + shareMessageIntent.putExtra(Intent.EXTRA_TEXT, shareString); // Set the MIME type. - shareIntent.setType("text/plain"); + shareMessageIntent.setType("text/plain"); // Set the intent to open in a new task. - shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + shareMessageIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Make it so. - startActivity(Intent.createChooser(shareIntent, getString(R.string.share_url))); + startActivity(Intent.createChooser(shareMessageIntent, getString(R.string.share_message))); + + // Consume the event. + return true; + } else if (menuItemId == R.id.share_url) { // Share URL. + // Create the share intent. + Intent shareUrlIntent = new Intent(Intent.ACTION_SEND); + + // Add the URL to the intent. + shareUrlIntent.putExtra(Intent.EXTRA_TEXT, currentWebView.getUrl()); + + // Add the title to the intent. + shareUrlIntent.putExtra(Intent.EXTRA_SUBJECT, currentWebView.getTitle()); + + // Set the MIME type. + shareUrlIntent.setType("text/plain"); + + // Set the intent to open in a new task. + shareUrlIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + //Make it so. + startActivity(Intent.createChooser(shareUrlIntent, getString(R.string.share_url))); // Consume the event. return true; @@ -1941,9 +1958,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook Uri currentUri = Uri.parse(currentWebView.getUrl()); String currentDomain = currentUri.getHost(); - // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`. - DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0); - // Create the domain and store the database ID. int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain); @@ -2838,21 +2852,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Initialize the formatted URL string. String url = ""; - // Check to see if `unformattedUrlString` is a valid URL. Otherwise, convert it into a search. + // Check to see if the unformatted URL string is a valid URL. Otherwise, convert it into a search. if (unformattedUrlString.startsWith("content://")) { // This is a Content URL. // Load the entire content URL. url = unformattedUrlString; } else if (Patterns.WEB_URL.matcher(unformattedUrlString).matches() || unformattedUrlString.startsWith("http://") || unformattedUrlString.startsWith("https://") || unformattedUrlString.startsWith("file://")) { // This is a standard URL. // Add `https://` at the beginning if there is no protocol. Otherwise the app will segfault. - if (!unformattedUrlString.startsWith("http") && !unformattedUrlString.startsWith("file://") && !unformattedUrlString.startsWith("content://")) { + if (!unformattedUrlString.startsWith("http") && !unformattedUrlString.startsWith("file://")) { unformattedUrlString = "https://" + unformattedUrlString; } - // Initialize `unformattedUrl`. + // Initialize the unformatted URL. URL unformattedUrl = null; - // Convert `unformattedUrlString` to a `URL`, then to a `URI`, and then back to a `String`, which sanitizes the input and adds in any missing components. + // Convert the unformatted URL string to a URL, then to a URI, and then back to a string, which sanitizes the input and adds in any missing components. try { unformattedUrl = new URL(unformattedUrlString); } catch (MalformedURLException e) { @@ -3026,7 +3040,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook currentWebView.loadUrl(temporaryMhtFile.toString()); } catch (Exception exception) { // Display a snackbar. - Snackbar.make(currentWebView, getString(R.string.error) + " " + exception.toString(), Snackbar.LENGTH_INDEFINITE).show(); + Snackbar.make(currentWebView, getString(R.string.error) + " " + exception, Snackbar.LENGTH_INDEFINITE).show(); } } else { // Let the WebView handle opening of the file. // Open the file. @@ -3371,9 +3385,6 @@ 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. The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`. - bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this, null, null, 0); - // Initialize `currentBookmarksFolder`. `""` is the home folder in the database. currentBookmarksFolder = ""; @@ -3437,7 +3448,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. @@ -3508,9 +3519,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); @@ -3539,8 +3549,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(); @@ -3708,10 +3736,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`. - DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0); - - // Get a full cursor from `domainsDatabaseHelper`. + // Get a full domain name cursor. Cursor domainNameCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomain(); // Initialize `domainSettingsSet`. @@ -3720,7 +3745,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the domain name column index. int domainNameColumnIndex = domainNameCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME); - // Populate `domainSettingsSet`. + // Populate the domain settings set. for (int i = 0; i < domainNameCursor.getCount(); i++) { // Move the domains cursor to the current row. domainNameCursor.moveToPosition(i); @@ -3729,7 +3754,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook domainSettingsSet.add(domainNameCursor.getString(domainNameColumnIndex)); } - // Close `domainNameCursor. + // Close the domain name cursor. domainNameCursor.close(); // Initialize the domain name in database variable. @@ -3748,7 +3773,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Check all the subdomains of the host name against wildcard domains in the domain cursor. - while (!nestedScrollWebView.getDomainSettingsApplied() && newHostName.contains(".")) { // Stop checking if domain settings are already applied or there are no more `.` in the host name. + while (!nestedScrollWebView.getDomainSettingsApplied() && newHostName.contains(".")) { // Stop checking if domain settings are already applied or there are no more `.` in the hostname. if (domainSettingsSet.contains("*." + newHostName)) { // Check the host name prepended by `*.`. // Set the domain settings applied tracker to true. nestedScrollWebView.setDomainSettingsApplied(true); @@ -3784,12 +3809,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String[] userAgentDataArray = getResources().getStringArray(R.array.user_agent_data); if (nestedScrollWebView.getDomainSettingsApplied()) { // The url has custom domain settings. - // Get a cursor for the current host and move it to the first position. + // Remove the incorrect lint warning below that the domain name in database might be null. + assert domainNameInDatabase != null; + + // Get a cursor for the current host. Cursor currentDomainSettingsCursor = domainsDatabaseHelper.getCursorForDomainName(domainNameInDatabase); + + // Move to the first position. currentDomainSettingsCursor.moveToFirst(); // Get the settings from the cursor. - nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper._ID))); + nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ID))); nestedScrollWebView.getSettings().setJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1); nestedScrollWebView.setAcceptCookies(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES)) == 1); nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1); @@ -4003,15 +4033,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook break; } - // Get the current theme status. - int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - // Set a background on the URL relative layout to indicate that custom domain settings are being used. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_light_green, null)); - } else { - urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_dark_blue, null)); - } + urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.domain_settings_url_background, null)); } else { // The new URL does not have custom domain settings. Load the defaults. // Store the values from the shared preferences. nestedScrollWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false)); @@ -4292,35 +4315,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook optionsPrivacyMenuItem.setIcon(R.drawable.privacy_mode); } - // Get the current theme status. - int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - // Update the cookies icon. - if (currentWebView.getAcceptCookies()) { // Cookies are enabled. + if (currentWebView.getAcceptCookies()) { optionsCookiesMenuItem.setIcon(R.drawable.cookies_enabled); - } else { // Cookies are disabled. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - optionsCookiesMenuItem.setIcon(R.drawable.cookies_disabled_day); - } else { - optionsCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night); - } + } else { + optionsCookiesMenuItem.setIcon(R.drawable.cookies_disabled); } // Update the refresh icon. if (optionsRefreshMenuItem.getTitle() == getString(R.string.refresh)) { // The refresh icon is displayed. - // Set the icon according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - optionsRefreshMenuItem.setIcon(R.drawable.refresh_enabled_day); - } else { - optionsRefreshMenuItem.setIcon(R.drawable.refresh_enabled_night); - } + // Set the icon. Once the minimum API is >= 26, the blue and black icons can be combined with a tint list. + optionsRefreshMenuItem.setIcon(R.drawable.refresh_enabled); } else { // The stop icon is displayed. - // Set the icon according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - optionsRefreshMenuItem.setIcon(R.drawable.close_blue_day); - } else { - optionsRefreshMenuItem.setIcon(R.drawable.close_blue_night); - } + // Set the icon. Once the minimum API is >= 26, the blue and black icons can be combined with a tint list. + optionsRefreshMenuItem.setIcon(R.drawable.close_blue); } // `invalidateOptionsMenu()` calls `onPrepareOptionsMenu()` and redraws the icons in the app bar. @@ -4486,49 +4494,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_")); - } + // Sanitize tracking queries. + if (sanitizeTrackingQueries) + url = sanitizeUrlHelper.sanitizeTrackingQueries(url); - // Remove `&utm_`. - if (url.contains("&utm_")) { - url = url.substring(0, url.indexOf("&utm_")); - } - } - - // 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; @@ -5014,16 +4986,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the background to indicate the domain settings status. if (currentWebView.getDomainSettingsApplied()) { - // Get the current theme status. - int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - - // Set a green background on the URL relative layout to indicate that custom domain settings are being used. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_light_green, null)); - } else { - urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_dark_blue, null)); - } + // Set a background on the URL relative layout to indicate that custom domain settings are being used. + urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.domain_settings_url_background, null)); } else { + // Remove any background on the URL relative layout. urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.color.transparent, null)); } } else { // The fragment has not been populated. Try again in 100 milliseconds. @@ -5130,7 +5096,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); @@ -5140,8 +5106,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. @@ -5157,6 +5126,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. @@ -5175,23 +5156,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); } } @@ -5205,6 +5195,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. @@ -5278,7 +5292,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()) { @@ -5289,23 +5303,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) { @@ -5918,8 +5915,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. @@ -5928,9 +5938,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); @@ -5977,17 +5984,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the app bar and theme preferences. boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean(getString(R.string.display_additional_app_bar_icons_key), false); - // If the icon is displayed in the AppBar, set it according to the theme. + // Set the icon if it is displayed in the AppBar. Once the minimum API is >= 26, the blue and black icons can be combined with a tint list. if (displayAdditionalAppBarIcons) { - // Get the current theme status. - int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - - // Set the stop icon according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - optionsRefreshMenuItem.setIcon(R.drawable.close_blue_day); - } else { - optionsRefreshMenuItem.setIcon(R.drawable.close_blue_night); - } + optionsRefreshMenuItem.setIcon(R.drawable.close_blue); } } } @@ -6009,15 +6008,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // If the icon is displayed in the app bar, reset it according to the theme. if (displayAdditionalAppBarIcons) { - // Get the current theme status. - int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - - // Set the icon according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - optionsRefreshMenuItem.setIcon(R.drawable.refresh_enabled_day); - } else { - optionsRefreshMenuItem.setIcon(R.drawable.refresh_enabled_night); - } + // Set the icon. + optionsRefreshMenuItem.setIcon(R.drawable.refresh_enabled); } } @@ -6126,7 +6118,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - // Handle SSL Certificate errors. + // Handle SSL Certificate errors. Suppress the lint warning that ignoring the error might be dangerous. + @SuppressLint("WebViewClientOnReceivedSslError") @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { // Get the current website SSL certificate. @@ -6145,11 +6138,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Proceed to the website if the current SSL website certificate matches the pinned domain certificate. if (nestedScrollWebView.hasPinnedSslCertificate()) { // Get the pinned SSL certificate. - ArrayList pinnedSslCertificateArrayList = nestedScrollWebView.getPinnedSslCertificate(); + Pair pinnedSslCertificatePair = nestedScrollWebView.getPinnedSslCertificate(); // Extract the arrays from the array list. - String[] pinnedSslCertificateStringArray = (String[]) pinnedSslCertificateArrayList.get(0); - Date[] pinnedSslCertificateDateArray = (Date[]) pinnedSslCertificateArrayList.get(1); + String[] pinnedSslCertificateStringArray = pinnedSslCertificatePair.getFirst(); + Date[] pinnedSslCertificateDateArray = pinnedSslCertificatePair.getSecond(); // Check if the current SSL certificate matches the pinned certificate. if (currentWebsiteIssuedToCName.equals(pinnedSslCertificateStringArray[0]) && currentWebsiteIssuedToOName.equals(pinnedSslCertificateStringArray[1]) &&