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=45a1de5b1c94e2c9267c6355eeaa8e9ccf113622;hp=d44636432e2057a9d321fb4facf72b95e9e89fd7;hb=1af1a793790badfcaf5497c82c4e7b70c7fdb69e;hpb=484f0b0f0143c53a4722cee3a31e714df0c8c49a 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 d4463643..45a1de5b 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; @@ -79,6 +79,7 @@ import android.webkit.SslErrorHandler; import android.webkit.ValueCallback; import android.webkit.WebBackForwardList; import android.webkit.WebChromeClient; +import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebStorage; @@ -117,6 +118,7 @@ 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; @@ -203,10 +205,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; @@ -440,7 +442,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 @@ -453,7 +455,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(); } } } @@ -528,9 +530,7 @@ 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. - if (Build.VERSION.SDK_INT >= 21) { - WebView.enableSlowWholeDocumentDraw(); - } + WebView.enableSlowWholeDocumentDraw(); // Set the theme. setTheme(R.style.PrivacyBrowser); @@ -978,9 +978,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Disable the clear form data menu item if the API >= 26 so that the status of the main Clear Data is calculated correctly. optionsClearFormDataMenuItem.setEnabled(Build.VERSION.SDK_INT < 26); - // Only display the dark WebView menu item if API >= 21. - optionsDarkWebViewMenuItem.setVisible(Build.VERSION.SDK_INT >= 21); - // Get the shared preferences. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); @@ -1003,17 +1000,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); } } @@ -1379,12 +1368,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onDismissed(Snackbar snackbar, int event) { if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed. - // Delete the cookies, which command varies by SDK. - if (Build.VERSION.SDK_INT < 21) { - cookieManager.removeAllCookie(); - } else { - cookieManager.removeAllCookies(null); - } + // Delete the cookies. + cookieManager.removeAllCookies(null); } } }) @@ -1811,7 +1796,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook assert printManager != null; // Create a print document adapter from the current WebView. - PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter(); + PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter(getString(R.string.print)); // Print the document. printManager.print(getString(R.string.privacy_browser_webpage), printDocumentAdapter, null); @@ -1865,24 +1850,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; @@ -1949,8 +1955,8 @@ 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); + // Initialize the database handler. + DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this); // Create the domain and store the database ID. int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain); @@ -2785,11 +2791,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook closeCurrentTab(); } else { // There isn't anything to do in Privacy Browser. // Close Privacy Browser. `finishAndRemoveTask()` also removes Privacy Browser from the recent app list. - if (Build.VERSION.SDK_INT >= 21) { - finishAndRemoveTask(); - } else { - finish(); - } + finishAndRemoveTask(); // Manually kill Privacy Browser. Otherwise, it is glitchy when restarted. System.exit(0); @@ -2805,11 +2807,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Run the commands that correlate to the specified request code. switch (requestCode) { case BROWSE_FILE_UPLOAD_REQUEST_CODE: - // File uploads only work on API >= 21. - if (Build.VERSION.SDK_INT >= 21) { - // Pass the file to the WebView. - fileChooserCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, returnedIntent)); - } + // Pass the file to the WebView. + fileChooserCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, returnedIntent)); break; case BROWSE_OPEN_REQUEST_CODE: @@ -2853,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) { @@ -3041,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. @@ -3095,18 +3094,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Remove the lint warning below that the input method manager might be null. assert inputMethodManager != null; - // Initialize the gray foreground color spans for highlighting the URLs. The deprecated `getResources()` must be used until API >= 23. - initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500)); - finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500)); + // Initialize the gray foreground color spans for highlighting the URLs. + initialGrayColorSpan = new ForegroundColorSpan(getColor(R.color.gray_500)); + finalGrayColorSpan = new ForegroundColorSpan(getColor(R.color.gray_500)); // Get the current theme status. int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; // Set the red color span according to the theme. if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700)); + redColorSpan = new ForegroundColorSpan(getColor(R.color.red_a700)); } else { - redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_900)); + redColorSpan = new ForegroundColorSpan(getColor(R.color.red_900)); } // Remove the formatting from the URL edit text when the user is editing the text. @@ -3386,8 +3385,8 @@ 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 the bookmarks database helper. + bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this); // Initialize `currentBookmarksFolder`. `""` is the home folder in the database. currentBookmarksFolder = ""; @@ -3723,8 +3722,8 @@ 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); + // Initialize the database handler. + DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this); // Get a full cursor from `domainsDatabaseHelper`. Cursor domainNameCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomain(); @@ -3735,7 +3734,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); @@ -3744,7 +3743,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. @@ -3763,7 +3762,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); @@ -3799,12 +3798,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); @@ -4018,15 +4022,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)); @@ -4307,35 +4304,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. @@ -4778,12 +4760,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Clear cookies. if (clearEverything || sharedPreferences.getBoolean("clear_cookies", true)) { - // The command to remove cookies changed slightly in API 21. - if (Build.VERSION.SDK_INT >= 21) { - CookieManager.getInstance().removeAllCookies(null); - } else { - CookieManager.getInstance().removeAllCookie(); - } + // Request the cookies be deleted. + CookieManager.getInstance().removeAllCookies(null); // Manually delete the cookies database, as `CookieManager` sometimes will not flush its changes to disk before `System.exit(0)` is run. try { @@ -4941,11 +4919,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Close Privacy Browser. `finishAndRemoveTask` also removes Privacy Browser from the recent app list. - if (Build.VERSION.SDK_INT >= 21) { - finishAndRemoveTask(); - } else { - finish(); - } + finishAndRemoveTask(); // Remove the terminated program from RAM. The status code is `0`. System.exit(0); @@ -5037,16 +5011,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,9 +5098,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook nestedScrollWebView.getSettings().setDisplayZoomControls(false); // Don't allow mixed content (HTTP and HTTPS) on the same website. - if (Build.VERSION.SDK_INT >= 21) { - nestedScrollWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW); - } + nestedScrollWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW); // Set the WebView to load in overview mode (zoomed out to the maximum width). nestedScrollWebView.getSettings().setLoadWithOverviewMode(true); @@ -5304,72 +5270,46 @@ 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. - // On API < 23, `getViewTreeObserver().addOnScrollChangedListener()` must be used, but it is a little bit buggy and appears to get garbage collected from time to time. - if (Build.VERSION.SDK_INT >= 23) { - nestedScrollWebView.setOnScrollChangeListener((view, scrollX, scrollY, oldScrollX, oldScrollY) -> { - // Set the swipe to refresh status. - if (nestedScrollWebView.getSwipeToRefresh()) { - // Only enable swipe to refresh if the WebView is scrolled to the top. - swipeRefreshLayout.setEnabled(nestedScrollWebView.getScrollY() == 0); - } else { - // Disable swipe to refresh. - 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); + nestedScrollWebView.setOnScrollChangeListener((view, scrollX, scrollY, oldScrollX, oldScrollY) -> { + // Set the swipe to refresh status. + if (nestedScrollWebView.getSwipeToRefresh()) { + // Only enable swipe to refresh if the WebView is scrolled to the top. + swipeRefreshLayout.setEnabled(nestedScrollWebView.getScrollY() == 0); + } else { + // Disable swipe to refresh. + swipeRefreshLayout.setEnabled(false); + } - // 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()); + // 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(); - } - } + // 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()); - // 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) { - /* Hide the system bars. - * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. - * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar. - * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. - * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. - */ - rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } - }); - } else { - nestedScrollWebView.getViewTreeObserver().addOnScrollChangedListener(() -> { - if (nestedScrollWebView.getSwipeToRefresh()) { - // Only enable swipe to refresh if the WebView is scrolled to the top. - swipeRefreshLayout.setEnabled(nestedScrollWebView.getScrollY() == 0); - } else { - // Disable swipe to refresh. - swipeRefreshLayout.setEnabled(false); + // 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) { - /* Hide the system bars. - * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. - * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar. - * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. - * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. - */ - rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } - }); - } + // 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) { + /* Hide the system bars. + * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. + * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar. + * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. + * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. + */ + rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } + }); // Set the web chrome client. nestedScrollWebView.setWebChromeClient(new WebChromeClient() { @@ -5501,34 +5441,31 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Upload files. @Override public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { - // Show the file chooser if the device is running API >= 21. - if (Build.VERSION.SDK_INT >= 21) { - // Store the file path callback. - fileChooserCallback = filePathCallback; + // Store the file path callback. + fileChooserCallback = filePathCallback; - // Create an intent to open a chooser based on the file chooser parameters. - Intent fileChooserIntent = fileChooserParams.createIntent(); + // Create an intent to open a chooser based on the file chooser parameters. + Intent fileChooserIntent = fileChooserParams.createIntent(); - // Get a handle for the package manager. - PackageManager packageManager = getPackageManager(); + // Get a handle for the package manager. + PackageManager packageManager = getPackageManager(); - // Check to see if the file chooser intent resolves to an installed package. - if (fileChooserIntent.resolveActivity(packageManager) != null) { // The file chooser intent is fine. - // Start the file chooser intent. - startActivityForResult(fileChooserIntent, BROWSE_FILE_UPLOAD_REQUEST_CODE); - } else { // The file chooser intent will cause a crash. - // Create a generic intent to open a chooser. - Intent genericFileChooserIntent = new Intent(Intent.ACTION_GET_CONTENT); + // Check to see if the file chooser intent resolves to an installed package. + if (fileChooserIntent.resolveActivity(packageManager) != null) { // The file chooser intent is fine. + // Start the file chooser intent. + startActivityForResult(fileChooserIntent, BROWSE_FILE_UPLOAD_REQUEST_CODE); + } else { // The file chooser intent will cause a crash. + // Create a generic intent to open a chooser. + Intent genericFileChooserIntent = new Intent(Intent.ACTION_GET_CONTENT); - // Request an openable file. - genericFileChooserIntent.addCategory(Intent.CATEGORY_OPENABLE); + // Request an openable file. + genericFileChooserIntent.addCategory(Intent.CATEGORY_OPENABLE); - // Set the file type to everything. - genericFileChooserIntent.setType("*/*"); + // Set the file type to everything. + genericFileChooserIntent.setType("*/*"); - // Start the generic file chooser intent. - startActivityForResult(genericFileChooserIntent, BROWSE_FILE_UPLOAD_REQUEST_CODE); - } + // Start the generic file chooser intent. + startActivityForResult(genericFileChooserIntent, BROWSE_FILE_UPLOAD_REQUEST_CODE); } return true; } @@ -5618,7 +5555,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Check requests against the block lists. The deprecated `shouldInterceptRequest()` must be used until minimum API >= 21. @Override - public WebResourceResponse shouldInterceptRequest(WebView view, String url) { + public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest webResourceRequest) { + // Get the URL. + String url = webResourceRequest.getUrl().toString(); + // Check to see if the resource request is for the main URL. if (url.equals(nestedScrollWebView.getCurrentUrl())) { // `return null` loads the resource request, which should never be blocked if it is the main URL. @@ -5638,9 +5578,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - // Sanitize the URL. - url = sanitizeUrl(url); - // Create an empty web resource response to be used if the resource request is blocked. WebResourceResponse emptyWebResourceResponse = new WebResourceResponse("text/plain", "utf8", new ByteArrayInputStream("".getBytes())); @@ -5656,31 +5593,25 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Store a copy of the current domain for use in later requests. String currentDomain = currentBaseDomain; - // Nobody is happy when comparing null strings. - if (url != null) { - // Convert the request URL to a URI. - Uri requestUri = Uri.parse(url); - - // Get the request host name. - String requestBaseDomain = requestUri.getHost(); + // Get the request host name. + String requestBaseDomain = webResourceRequest.getUrl().getHost(); - // Only check for third-party requests if the current base domain is not empty and the request domain is not null. - if (!currentBaseDomain.isEmpty() && (requestBaseDomain != null)) { - // Determine the current base domain. - while (currentBaseDomain.indexOf(".", currentBaseDomain.indexOf(".") + 1) > 0) { // There is at least one subdomain. - // Remove the first subdomain. - currentBaseDomain = currentBaseDomain.substring(currentBaseDomain.indexOf(".") + 1); - } - - // Determine the request base domain. - while (requestBaseDomain.indexOf(".", requestBaseDomain.indexOf(".") + 1) > 0) { // There is at least one subdomain. - // Remove the first subdomain. - requestBaseDomain = requestBaseDomain.substring(requestBaseDomain.indexOf(".") + 1); - } + // Only check for third-party requests if the current base domain is not empty and the request domain is not null. + if (!currentBaseDomain.isEmpty() && (requestBaseDomain != null)) { + // Determine the current base domain. + while (currentBaseDomain.indexOf(".", currentBaseDomain.indexOf(".") + 1) > 0) { // There is at least one subdomain. + // Remove the first subdomain. + currentBaseDomain = currentBaseDomain.substring(currentBaseDomain.indexOf(".") + 1); + } - // Update the third party request tracker. - isThirdPartyRequest = !currentBaseDomain.equals(requestBaseDomain); + // Determine the request base domain. + while (requestBaseDomain.indexOf(".", requestBaseDomain.indexOf(".") + 1) > 0) { // There is at least one subdomain. + // Remove the first subdomain. + requestBaseDomain = requestBaseDomain.substring(requestBaseDomain.indexOf(".") + 1); } + + // Update the third party request tracker. + isThirdPartyRequest = !currentBaseDomain.equals(requestBaseDomain); } // Get the current WebView page position. @@ -6037,17 +5968,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); } } } @@ -6055,7 +5978,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onPageFinished(WebView view, String url) { // Flush any cookies to persistent storage. The cookie manager has become very lazy about flushing cookies in recent versions. - if (nestedScrollWebView.getAcceptCookies() && Build.VERSION.SDK_INT >= 21) { + if (nestedScrollWebView.getAcceptCookies()) { CookieManager.getInstance().flush(); } @@ -6069,15 +5992,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); } } @@ -6186,7 +6102,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. @@ -6205,11 +6122,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]) &&