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=e61ab7dbd8882a9781092cea28a2466af0aad730;hp=45a1de5b1c94e2c9267c6355eeaa8e9ccf113622;hb=eac401700379e6ac5e6b0b696b932354001ef738;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..e61ab7db 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -1,5 +1,5 @@ /* - * Copyright © 2015-2022 Soren Stoutner . + * Copyright 2015-2022 Soren Stoutner . * * Download cookie code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner . * @@ -52,7 +52,6 @@ import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; -import android.preference.PreferenceManager; import android.print.PrintDocumentAdapter; import android.print.PrintManager; import android.provider.DocumentsContract; @@ -99,6 +98,7 @@ import android.widget.RadioButton; import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.activity.OnBackPressedCallback; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; @@ -114,11 +114,11 @@ import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; +import androidx.preference.PreferenceManager; 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 +153,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,28 +175,28 @@ 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, WebViewTabFragment.NewTabListener { // Define the public static variables. - public static ExecutorService executorService = Executors.newFixedThreadPool(4); + public static final ExecutorService executorService = Executors.newFixedThreadPool(4); public static String orbotStatus = "unknown"; - public static ArrayList pendingDialogsArrayList = new ArrayList<>(); + public static final ArrayList pendingDialogsArrayList = new ArrayList<>(); 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 +235,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,14 +271,15 @@ 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 displayAdditionalAppBarIcons; private boolean displayingFullScreenVideo; private boolean downloadWithExternalApp; private boolean fullScreenBrowsingModeEnabled; @@ -289,9 +288,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; @@ -370,7 +370,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private Activity resultLauncherActivityHandle; // Define the save URL activity result launcher. It must be defined before `onCreate()` is run or the app will crash. - private final ActivityResultLauncher saveUrlActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(), + private final ActivityResultLauncher saveUrlActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument("*/*"), new ActivityResultCallback() { @Override public void onActivityResult(Uri fileUri) { @@ -385,17 +385,36 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook }); // Define the save webpage archive activity result launcher. It must be defined before `onCreate()` is run or the app will crash. - private final ActivityResultLauncher saveWebpageArchiveActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(), + private final ActivityResultLauncher saveWebpageArchiveActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument("multipart/related"), new ActivityResultCallback() { @Override public void onActivityResult(Uri fileUri) { // Only save the webpage archive if the file URI is not null, which happens if the user exited the file picker by pressing back. if (fileUri != null) { + // Initialize the file name string from the file URI last path segment. + String temporaryFileNameString = fileUri.getLastPathSegment(); + + // Query the exact file name if the API >= 26. + if (Build.VERSION.SDK_INT >= 26) { + // Get a cursor from the content resolver. + Cursor contentResolverCursor = resultLauncherActivityHandle.getContentResolver().query(fileUri, null, null, null); + + // Move to the fist row. + contentResolverCursor.moveToFirst(); + + // Get the file name from the cursor. + temporaryFileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)); + + // Close the cursor. + contentResolverCursor.close(); + } + + // Save the final file name string so it can be used inside the lambdas. This will no longer be needed once this activity has transitioned to Kotlin. + String finalFileNameString = temporaryFileNameString; + try { // Create a temporary MHT file. File temporaryMhtFile = File.createTempFile("temporary_mht_file", ".mht", getCacheDir()); - - // Save the temporary MHT file. currentWebView.saveWebArchive(temporaryMhtFile.toString(), false, callbackValue -> { if (callbackValue != null) { // The temporary MHT file was saved successfully. try { @@ -420,29 +439,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook mhtOutputStream.close(); temporaryMhtFileInputStream.close(); - // Initialize the file name string from the file URI last path segment. - String fileNameString = fileUri.getLastPathSegment(); - - // Query the exact file name if the API >= 26. - if (Build.VERSION.SDK_INT >= 26) { - // Get a cursor from the content resolver. - Cursor contentResolverCursor = resultLauncherActivityHandle.getContentResolver().query(fileUri, null, null, null); - - // Move to the fist row. - contentResolverCursor.moveToFirst(); - - // Get the file name from the cursor. - fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)); - - // Close the cursor. - contentResolverCursor.close(); - } - // Display a snackbar. - Snackbar.make(currentWebView, getString(R.string.file_saved) + " " + fileNameString, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(currentWebView, getString(R.string.saved, finalFileNameString), Snackbar.LENGTH_SHORT).show(); } catch (Exception exception) { // Display a snackbar with the exception. - Snackbar.make(currentWebView, getString(R.string.error_saving_file) + " " + exception, Snackbar.LENGTH_INDEFINITE).show(); + Snackbar.make(currentWebView, getString(R.string.error_saving_file, finalFileNameString, exception), Snackbar.LENGTH_INDEFINITE).show(); } finally { // Delete the temporary MHT file. //noinspection ResultOfMethodCallIgnored @@ -450,19 +451,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } else { // There was an unspecified error while saving the temporary MHT file. // Display an error snackbar. - Snackbar.make(currentWebView, getString(R.string.error_saving_file), Snackbar.LENGTH_INDEFINITE).show(); + Snackbar.make(currentWebView, getString(R.string.error_saving_file, finalFileNameString, getString(R.string.unknown_error)), Snackbar.LENGTH_INDEFINITE).show(); } }); } catch (IOException ioException) { // Display a snackbar with the IO exception. - Snackbar.make(currentWebView, getString(R.string.error_saving_file) + " " + ioException, Snackbar.LENGTH_INDEFINITE).show(); + Snackbar.make(currentWebView, getString(R.string.error_saving_file, finalFileNameString, ioException), Snackbar.LENGTH_INDEFINITE).show(); } } } }); // Define the save webpage image activity result launcher. It must be defined before `onCreate()` is run or the app will crash. - private final ActivityResultLauncher saveWebpageImageActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(), + private final ActivityResultLauncher saveWebpageImageActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument("image/png"), new ActivityResultCallback() { @Override public void onActivityResult(Uri fileUri) { @@ -500,9 +501,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); // Get the preferences. - String appTheme = sharedPreferences.getString("app_theme", getString(R.string.app_theme_default_value)); + String appTheme = sharedPreferences.getString(getString(R.string.app_theme_key), getString(R.string.app_theme_default_value)); boolean allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false); bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false); + displayAdditionalAppBarIcons = sharedPreferences.getBoolean(getString(R.string.display_additional_app_bar_icons_key), false); // Get the theme entry values string array. String[] appThemeEntryValuesStringArray = getResources().getStringArray(R.array.app_theme_entry_values); @@ -532,15 +534,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); @@ -594,6 +590,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Initially disable the sliding drawers. They will be enabled once the blocklists are loaded. drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); + // Initially hide the user interface so that only the blocklist loading screen is shown (if reloading). + drawerLayout.setVisibility(View.GONE); + // Initialize the web view pager adapter. webViewPagerAdapter = new WebViewPagerAdapter(getSupportFragmentManager()); @@ -603,8 +602,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(); @@ -612,6 +614,48 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Apply the app settings from the shared preferences. applyAppSettings(); + // Control what the system back command does. + OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) { + @Override + public void handleOnBackPressed() { + // Process the different back options. + if (drawerLayout.isDrawerVisible(GravityCompat.START)) { // The navigation drawer is open. + // Close the navigation drawer. + drawerLayout.closeDrawer(GravityCompat.START); + } else if (drawerLayout.isDrawerVisible(GravityCompat.END)){ // The bookmarks drawer is open. + // close the bookmarks drawer. + drawerLayout.closeDrawer(GravityCompat.END); + } else if (displayingFullScreenVideo) { // A full screen video is shown. + // Exit the full screen video. + exitFullScreenVideo(); + } else if (currentWebView.canGoBack()) { // There is at least one item in the current WebView history. + // Get the current web back forward list. + WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList(); + + // Get the previous entry URL. + String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl(); + + // Apply the domain settings. + applyDomainSettings(currentWebView, previousUrl, false, false, false); + + // Go back. + currentWebView.goBack(); + } else if (tabLayout.getTabCount() > 1) { // There are at least two tabs. + // Close the current tab. + closeCurrentTab(); + } else { // There isn't anything to do in Privacy Browser. + // Close Privacy Browser. `finishAndRemoveTask()` also removes Privacy Browser from the recent app list. + finishAndRemoveTask(); + + // Manually kill Privacy Browser. Otherwise, it is glitchy when restarted. + System.exit(0); + } + } + }; + + // Register the on back pressed callback. + getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback); + // Populate the blocklists. populateBlocklists = new PopulateBlocklists(this, this).execute(); } @@ -621,9 +665,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. @@ -674,7 +715,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Add a new tab if specified in the preferences. - if (sharedPreferences.getBoolean("open_intents_in_new_tab", true)) { // Load the URL in a new tab. + if (sharedPreferences.getBoolean(getString(R.string.open_intents_in_new_tab_key), true)) { // Load the URL in a new tab. // Set the loading new intent flag. loadingNewIntent = true; @@ -695,6 +736,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); } } @@ -978,11 +1022,8 @@ 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); - // Get the shared preferences. - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - - // Get the dark theme and app bar preferences. - boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean(getString(R.string.display_additional_app_bar_icons_key), false); + // Only display the dark WebView menu item if the API >= 29. + optionsDarkWebViewMenuItem.setVisible(Build.VERSION.SDK_INT >= 29); // Set the status of the additional app bar icons. Setting the refresh menu item to `SHOW_AS_ACTION_ALWAYS` makes it appear even on small devices like phones. if (displayAdditionalAppBarIcons) { @@ -1061,10 +1102,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Enable DOM Storage if JavaScript is enabled. optionsDomStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled()); - // Set the checkbox status for dark WebView if the WebView supports it. - if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { - optionsDarkWebViewMenuItem.setChecked(WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON); - } + // Get the current theme status. + int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + + // Enable dark WebView if night mode is enabled. + optionsDarkWebViewMenuItem.setEnabled(currentThemeStatus == Configuration.UI_MODE_NIGHT_YES); + + // Set the checkbox status for dark WebView if the device is running API >= 29 and algorithmic darkening is supported. + if ((Build.VERSION.SDK_INT >= 29) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) + optionsDarkWebViewMenuItem.setChecked(WebSettingsCompat.isAlgorithmicDarkeningAllowed(currentWebView.getSettings())); } // Set the cookies menu item checked status. @@ -1688,7 +1734,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; } else if (menuItemId == R.id.user_agent_custom) { // User Agent - Custom. // Update the user agent. - currentWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value))); + currentWebView.getSettings().setUserAgentString(sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value))); // Reload the current WebView. currentWebView.reload(); @@ -1741,17 +1787,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Consume the event. return true; } else if (menuItemId == R.id.dark_webview) { // Dark WebView. - // Check to see if dark WebView is supported by this WebView. - if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { - // Toggle the dark WebView setting. - if (WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON) { // Dark WebView is currently enabled. - // Turn off dark WebView. - WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); - } else { // Dark WebView is currently disabled. - // Turn on dark WebView. - WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); - } - } + // Toggle dark WebView if supported. + if ((Build.VERSION.SDK_INT >= 29) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) + WebSettingsCompat.setAlgorithmicDarkeningAllowed(currentWebView.getSettings(), !WebSettingsCompat.isAlgorithmicDarkeningAllowed(currentWebView.getSettings())); // Consume the event. return true; @@ -1905,19 +1943,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Consume the event. return true; } else if (menuItemId == R.id.add_or_edit_domain) { // Add or edit domain. + // Reapply the domain settings on returning to `MainWebViewActivity`. + reapplyDomainSettingsOnRestart = true; + // Check if domain settings currently exist. if (currentWebView.getDomainSettingsApplied()) { // Edit the current domain settings. - // Reapply the domain settings on returning to `MainWebViewActivity`. - reapplyDomainSettingsOnRestart = true; - // Create an intent to launch the domains activity. Intent domainsIntent = new Intent(this, DomainsActivity.class); // Add the extra information to the intent. - domainsIntent.putExtra("load_domain", currentWebView.getDomainSettingsDatabaseId()); - domainsIntent.putExtra("close_on_back", true); - domainsIntent.putExtra("current_url", currentWebView.getUrl()); - domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses()); + domainsIntent.putExtra(DomainsActivity.LOAD_DOMAIN, currentWebView.getDomainSettingsDatabaseId()); + domainsIntent.putExtra(DomainsActivity.CLOSE_ON_BACK, true); + domainsIntent.putExtra(DomainsActivity.CURRENT_URL, currentWebView.getUrl()); + domainsIntent.putExtra(DomainsActivity.CURRENT_IP_ADDRESSES, currentWebView.getCurrentIpAddresses()); // Get the current certificate. SslCertificate sslCertificate = currentWebView.getCertificate(); @@ -1935,28 +1973,28 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook long endDateLong = sslCertificate.getValidNotAfterDate().getTime(); // Add the certificate to the intent. - domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName); - domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName); - domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName); - domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName); - domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName); - domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName); - domainsIntent.putExtra("ssl_start_date", startDateLong); - domainsIntent.putExtra("ssl_end_date", endDateLong); + domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_TO_CNAME, issuedToCName); + domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_TO_ONAME, issuedToOName); + domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_TO_UNAME, issuedToUName); + domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_BY_CNAME, issuedByCName); + domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_BY_ONAME, issuedByOName); + domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_BY_UNAME, issuedByUName); + domainsIntent.putExtra(DomainsActivity.SSL_START_DATE, startDateLong); + domainsIntent.putExtra(DomainsActivity.SSL_END_DATE, endDateLong); } // Make it so. startActivity(domainsIntent); } else { // Add a new domain. - // Apply the new domain settings on returning to `MainWebViewActivity`. - reapplyDomainSettingsOnRestart = true; - - // Get the current domain + // Get the current URI. Uri currentUri = Uri.parse(currentWebView.getUrl()); + + // Get the current domain from the URI. String currentDomain = currentUri.getHost(); - // Initialize the database handler. - DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this); + // Set an empty domain if it is null. + if (currentDomain == null) + currentDomain = ""; // Create the domain and store the database ID. int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain); @@ -1965,10 +2003,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook Intent domainsIntent = new Intent(this, DomainsActivity.class); // Add the extra information to the intent. - domainsIntent.putExtra("load_domain", newDomainDatabaseId); - domainsIntent.putExtra("close_on_back", true); - domainsIntent.putExtra("current_url", currentWebView.getUrl()); - domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses()); + domainsIntent.putExtra(DomainsActivity.LOAD_DOMAIN, newDomainDatabaseId); + domainsIntent.putExtra(DomainsActivity.CLOSE_ON_BACK, true); + domainsIntent.putExtra(DomainsActivity.CURRENT_URL, currentWebView.getUrl()); + domainsIntent.putExtra(DomainsActivity.CURRENT_IP_ADDRESSES, currentWebView.getCurrentIpAddresses()); // Get the current certificate. SslCertificate sslCertificate = currentWebView.getCertificate(); @@ -1986,14 +2024,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook long endDateLong = sslCertificate.getValidNotAfterDate().getTime(); // Add the certificate to the intent. - domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName); - domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName); - domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName); - domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName); - domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName); - domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName); - domainsIntent.putExtra("ssl_start_date", startDateLong); - domainsIntent.putExtra("ssl_end_date", endDateLong); + domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_TO_CNAME, issuedToCName); + domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_TO_ONAME, issuedToOName); + domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_TO_UNAME, issuedToUName); + domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_BY_CNAME, issuedByCName); + domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_BY_ONAME, issuedByOName); + domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_BY_UNAME, issuedByUName); + domainsIntent.putExtra(DomainsActivity.SSL_START_DATE, startDateLong); + domainsIntent.putExtra(DomainsActivity.SSL_END_DATE, endDateLong); } // Make it so. @@ -2131,8 +2169,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook Intent domainsIntent = new Intent(this, DomainsActivity.class); // Add the extra information to the intent. - domainsIntent.putExtra("current_url", currentWebView.getUrl()); - domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses()); + domainsIntent.putExtra(DomainsActivity.CURRENT_URL, currentWebView.getUrl()); + domainsIntent.putExtra(DomainsActivity.CURRENT_IP_ADDRESSES, currentWebView.getCurrentIpAddresses()); // Get the current certificate. SslCertificate sslCertificate = currentWebView.getCertificate(); @@ -2184,6 +2222,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Make it so. startActivity(logcatIntent); + } else if (menuItemId == R.id.webview_devtools) { // WebView Dev. + // Create a WebView DevTools intent. + Intent webViewDevToolsIntent = new Intent("com.android.webview.SHOW_DEV_UI"); + + // Launch as a new task so that the WebView DevTools and Privacy Browser show as a separate windows in the recent tasks list. + webViewDevToolsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // Make it so. + startActivity(webViewDevToolsIntent); } else if (menuItemId == R.id.guide) { // Guide. // Create an intent to launch the guide activity. Intent guideIntent = new Intent(this, GuideActivity.class); @@ -2663,7 +2710,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the bookmarks cursor with the current contents of this folder. bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentBookmarksFolder); - // Update the `ListView`. + // Update the list view. bookmarksCursorAdapter.changeCursor(bookmarksCursor); // Scroll to the new folder. @@ -2757,47 +2804,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the bookmarks cursor with the current contents of this folder. bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentBookmarksFolder); - // Update the `ListView`. + // Update the list view. bookmarksCursorAdapter.changeCursor(bookmarksCursor); } - // Override `onBackPressed()` to handle the navigation drawer and and the WebViews. - @Override - public void onBackPressed() { - // Check the different options for processing `back`. - if (drawerLayout.isDrawerVisible(GravityCompat.START)) { // The navigation drawer is open. - // Close the navigation drawer. - drawerLayout.closeDrawer(GravityCompat.START); - } else if (drawerLayout.isDrawerVisible(GravityCompat.END)){ // The bookmarks drawer is open. - // close the bookmarks drawer. - drawerLayout.closeDrawer(GravityCompat.END); - } else if (displayingFullScreenVideo) { // A full screen video is shown. - // Exit the full screen video. - exitFullScreenVideo(); - } else if (currentWebView.canGoBack()) { // There is at least one item in the current WebView history. - // Get the current web back forward list. - WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList(); - - // Get the previous entry URL. - String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl(); - - // Apply the domain settings. - applyDomainSettings(currentWebView, previousUrl, false, false, false); - - // Go back. - currentWebView.goBack(); - } else if (tabLayout.getTabCount() > 1) { // There are at least two tabs. - // Close the current tab. - closeCurrentTab(); - } else { // There isn't anything to do in Privacy Browser. - // Close Privacy Browser. `finishAndRemoveTask()` also removes Privacy Browser from the recent app list. - finishAndRemoveTask(); - - // Manually kill Privacy Browser. Otherwise, it is glitchy when restarted. - System.exit(0); - } - } - // Process the results of a file browse. @Override public void onActivityResult(int requestCode, int resultCode, Intent returnedIntent) { @@ -3094,19 +3104,10 @@ 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. + // Initialize the 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(getColor(R.color.red_a700)); - } else { - redColorSpan = new ForegroundColorSpan(getColor(R.color.red_900)); - } + redColorSpan = new ForegroundColorSpan(getColor(R.color.red_text)); // Remove the formatting from the URL edit text when the user is editing the text. urlEditText.setOnFocusChangeListener((View v, boolean hasFocus) -> { @@ -3385,15 +3386,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; @@ -3447,11 +3443,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Move the bookmark cursor to the first row. bookmarkCursor.moveToFirst(); - // Load the bookmark in a new tab but do not switch to the tab or close the drawer. - addNewTab(bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)), false); + // Load the bookmark in a new tab. + addNewTab(bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)), true); - // Display a snackbar. - Snackbar.make(currentWebView, R.string.bookmark_opened_in_background, Snackbar.LENGTH_SHORT).show(); + // Close the bookmarks drawer. + drawerLayout.closeDrawer(GravityCompat.END); } // Consume the event. @@ -3500,9 +3496,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); @@ -3521,14 +3514,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); // 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); - proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value)); - fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false); + incognitoModeEnabled = sharedPreferences.getBoolean(getString(R.string.incognito_mode_key), false); + sanitizeTrackingQueries = sharedPreferences.getBoolean(getString(R.string.tracking_queries_key), true); + sanitizeAmpRedirects = sharedPreferences.getBoolean(getString(R.string.amp_redirects_key), true); + proxyMode = sharedPreferences.getString(getString(R.string.proxy_key), getString(R.string.proxy_default_value)); + fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean(getString(R.string.full_screen_browsing_mode_key), false); + hideAppBar = sharedPreferences.getBoolean(getString(R.string.hide_app_bar_key), true); downloadWithExternalApp = sharedPreferences.getBoolean(getString(R.string.download_with_external_app_key), false); - hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true); scrollAppBar = sharedPreferences.getBoolean(getString(R.string.scroll_app_bar_key), true); // Apply the saved proxy mode if the app has been restarted. @@ -3541,20 +3533,37 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Get the search string. - String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value)); + String searchString = sharedPreferences.getString(getString(R.string.search_key), getString(R.string.search_default_value)); // Set the search string. - if (searchString.equals("Custom URL")) { // A custom search string is used. - searchURL = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value)); - } else { // A custom search string is not used. + if (searchString.equals(getString(R.string.custom_url_item))) + searchURL = sharedPreferences.getString(getString(R.string.search_custom_url_key), getString(R.string.search_custom_url_default_value)); + else searchURL = searchString; - } // 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 +3731,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,12 +3786,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); // Store the general preference information. - 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); - String webViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value)); - boolean wideViewport = sharedPreferences.getBoolean("wide_viewport", true); - boolean displayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true); + String defaultFontSizeString = sharedPreferences.getString(getString(R.string.font_size_key), getString(R.string.font_size_default_value)); + String defaultUserAgentName = sharedPreferences.getString(getString(R.string.user_agent_key), getString(R.string.user_agent_default_value)); + boolean defaultSwipeToRefresh = sharedPreferences.getBoolean(getString(R.string.swipe_to_refresh_key), true); + String webViewTheme = sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value)); + boolean wideViewport = sharedPreferences.getBoolean(getString(R.string.wide_viewport_key), true); + boolean displayWebpageImages = sharedPreferences.getBoolean(getString(R.string.display_webpage_images_key), true); // Get the WebView theme entry values string array. String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values); @@ -3816,12 +3822,14 @@ 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 fontSize = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE)); int swipeToRefreshInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH)); @@ -3894,7 +3902,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case SETTINGS_CUSTOM_USER_AGENT: // Set the default custom user agent. - nestedScrollWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value))); + nestedScrollWebView.getSettings().setUserAgentString(sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value))); break; default: @@ -3951,43 +3959,38 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Disable swipe to refresh. swipeRefreshLayout.setEnabled(false); + break; } - // Check to see if WebView themes are supported. - if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { + // Set the WebView theme if device is running API >= 29 and algorithmic darkening is supported. + if ((Build.VERSION.SDK_INT >= 29) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) { // Set the WebView theme. switch (webViewThemeInt) { case DomainsDatabaseHelper.SYSTEM_DEFAULT: // Set the WebView theme. A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant. if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) { // The light theme is selected. - // Turn off the WebView dark mode. - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); + // Turn off algorithmic darkening. + WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), false); } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected. - // Turn on the WebView dark mode. - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); + // Turn on algorithmic darkening. + WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), true); } else { // The system default theme is selected. // Get the current system theme status. int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - // Set the WebView theme according to the current system theme status. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode. - // Turn off the WebView dark mode. - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); - } else { // The system is in night mode. - // Turn on the WebView dark mode. - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); - } + // Set the algorithmic darkening according to the current system theme status. + WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES)); } break; case DomainsDatabaseHelper.LIGHT_THEME: - // Turn off the WebView dark mode. - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); + // Turn off algorithmic darkening. + WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), false); break; case DomainsDatabaseHelper.DARK_THEME: - // Turn on the WebView dark mode. - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); + // Turn on algorithmic darkening. + WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), true); break; } } @@ -4026,17 +4029,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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)); + nestedScrollWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean(getString(R.string.javascript_key), false)); nestedScrollWebView.setAcceptCookies(sharedPreferences.getBoolean(getString(R.string.cookies_key), false)); - nestedScrollWebView.getSettings().setDomStorageEnabled(sharedPreferences.getBoolean("dom_storage", false)); - boolean saveFormData = sharedPreferences.getBoolean("save_form_data", false); // Form data can be removed once the minimum API >= 26. - nestedScrollWebView.setEasyListEnabled(sharedPreferences.getBoolean("easylist", true)); - nestedScrollWebView.setEasyPrivacyEnabled(sharedPreferences.getBoolean("easyprivacy", true)); - nestedScrollWebView.setFanboysAnnoyanceListEnabled(sharedPreferences.getBoolean("fanboys_annoyance_list", true)); - nestedScrollWebView.setFanboysSocialBlockingListEnabled(sharedPreferences.getBoolean("fanboys_social_blocking_list", true)); - nestedScrollWebView.setUltraListEnabled(sharedPreferences.getBoolean("ultralist", true)); - nestedScrollWebView.setUltraPrivacyEnabled(sharedPreferences.getBoolean("ultraprivacy", true)); - nestedScrollWebView.setBlockAllThirdPartyRequests(sharedPreferences.getBoolean("block_all_third_party_requests", false)); + nestedScrollWebView.getSettings().setDomStorageEnabled(sharedPreferences.getBoolean(getString(R.string.dom_storage_key), false)); + boolean saveFormData = sharedPreferences.getBoolean(getString(R.string.save_form_data_key), false); // Form data can be removed once the minimum API >= 26. + nestedScrollWebView.setEasyListEnabled(sharedPreferences.getBoolean(getString(R.string.easylist_key), true)); + nestedScrollWebView.setEasyPrivacyEnabled(sharedPreferences.getBoolean(getString(R.string.easyprivacy_key), true)); + nestedScrollWebView.setFanboysAnnoyanceListEnabled(sharedPreferences.getBoolean(getString(R.string.fanboys_annoyance_list_key), true)); + nestedScrollWebView.setFanboysSocialBlockingListEnabled(sharedPreferences.getBoolean(getString(R.string.fanboys_social_blocking_list_key), true)); + nestedScrollWebView.setUltraListEnabled(sharedPreferences.getBoolean(getString(R.string.ultralist_key), true)); + nestedScrollWebView.setUltraPrivacyEnabled(sharedPreferences.getBoolean(getString(R.string.ultraprivacy_key), true)); + nestedScrollWebView.setBlockAllThirdPartyRequests(sharedPreferences.getBoolean(getString(R.string.block_all_third_party_requests_key), false)); // Apply the default cookie setting. cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptCookies()); @@ -4087,7 +4090,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case SETTINGS_CUSTOM_USER_AGENT: // Set the default custom user agent. - nestedScrollWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value))); + nestedScrollWebView.getSettings().setUserAgentString(sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value))); break; default: @@ -4095,27 +4098,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]); } - // Apply the WebView theme if supported by the installed WebView. - if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { + // Set the WebView theme if device is running API >= 29 and algorithmic darkening is supported. + if ((Build.VERSION.SDK_INT >= 29) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) { // Set the WebView theme. A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant. - if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) { // The light theme is selected. - // Turn off the WebView dark mode. - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); + if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) { // the light theme is selected. + // Turn off algorithmic darkening. + WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), false); } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected. - // Turn on the WebView dark mode. - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); + // Turn on algorithmic darkening. + WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), true); } else { // The system default theme is selected. // Get the current system theme status. int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - // Set the WebView theme according to the current system theme status. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode. - // Turn off the WebView dark mode. - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); - } else { // The system is in night mode. - // Turn on the WebView dark mode. - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); - } + // Set the algorithmic darkening according to the current system theme status. + WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), currentThemeStatus == Configuration.UI_MODE_NIGHT_YES); } } @@ -4143,7 +4140,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); } } @@ -4234,27 +4231,31 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } else { appBarLayout.setBackgroundResource(R.color.dark_blue_30); } + // Get the package manager. + PackageManager packageManager = getPackageManager(); // Check to see if I2P is installed. try { - // Get the package manager. - PackageManager packageManager = getPackageManager(); - - // Check to see if I2P is in the list. This will throw an error and drop to the catch section if it isn't installed. + // Check to see if the F-Droid flavor is installed. This will throw an error and drop to the catch section if it isn't installed. packageManager.getPackageInfo("net.i2p.android.router", 0); - } catch (PackageManager.NameNotFoundException exception) { // I2P is not installed. - // Sow the I2P not installed dialog if it is not already displayed. - if (getSupportFragmentManager().findFragmentByTag(getString(R.string.proxy_not_installed_dialog)) == null) { - // Get a handle for the waiting for proxy alert dialog. - DialogFragment i2pNotInstalledDialogFragment = ProxyNotInstalledDialog.displayDialog(proxyMode); + } catch (PackageManager.NameNotFoundException fdroidException) { // The F-Droid flavor is not installed. + try { + // Check to see if the Google Play flavor is installed. This will throw an error and drop to the catch section if it isn't installed. + packageManager.getPackageInfo("net.i2p.android", 0); + } catch (PackageManager.NameNotFoundException googlePlayException) { // The Google Play flavor is not installed. + // Sow the I2P not installed dialog if it is not already displayed. + if (getSupportFragmentManager().findFragmentByTag(getString(R.string.proxy_not_installed_dialog)) == null) { + // Get a handle for the waiting for proxy alert dialog. + DialogFragment i2pNotInstalledDialogFragment = ProxyNotInstalledDialog.displayDialog(proxyMode); - // Try to show the dialog. Sometimes the window is not yet active if returning from Settings. - try { - // Display the I2P not installed alert dialog. - i2pNotInstalledDialogFragment.show(getSupportFragmentManager(), getString(R.string.proxy_not_installed_dialog)); - } catch (Exception i2pNotInstalledException) { - // Add the dialog to the pending dialog array list. It will be displayed in `onStart()`. - pendingDialogsArrayList.add(new PendingDialog(i2pNotInstalledDialogFragment, getString(R.string.proxy_not_installed_dialog))); + // Try to show the dialog. Sometimes the window is not yet active if returning from Settings. + try { + // Display the I2P not installed alert dialog. + i2pNotInstalledDialogFragment.show(getSupportFragmentManager(), getString(R.string.proxy_not_installed_dialog)); + } catch (Exception i2pNotInstalledException) { + // Add the dialog to the pending dialog array list. It will be displayed in `onStart()`. + pendingDialogsArrayList.add(new PendingDialog(i2pNotInstalledDialogFragment, getString(R.string.proxy_not_installed_dialog))); + } } } } @@ -4483,49 +4484,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 Facebook Click IDs. - if (sanitizeFacebookClickIds) { - // Remove `?fbclid=`. - if (url.contains("?fbclid=")) { - url = url.substring(0, url.indexOf("?fbclid=")); - } + // Sanitize tracking queries. + if (sanitizeTrackingQueries) + url = sanitizeUrlHelper.sanitizeTrackingQueries(url); - // 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; @@ -4621,7 +4586,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Add a new tab if specified in the preferences. - if (sharedPreferences.getBoolean("open_intents_in_new_tab", true)) { // Load the URL in a new tab. + if (sharedPreferences.getBoolean(getString(R.string.open_intents_in_new_tab_key), true)) { // Load the URL in a new tab. // Set the loading new intent flag. loadingNewIntent = true; @@ -4749,7 +4714,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook bookmarksDatabaseHelper.close(); // Get the status of the clear everything preference. - boolean clearEverything = sharedPreferences.getBoolean("clear_everything", true); + boolean clearEverything = sharedPreferences.getBoolean(getString(R.string.clear_everything_key), true); // Get a handle for the runtime. Runtime runtime = Runtime.getRuntime(); @@ -4759,7 +4724,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String privateDataDirectoryString = getApplicationInfo().dataDir; // Clear cookies. - if (clearEverything || sharedPreferences.getBoolean("clear_cookies", true)) { + if (clearEverything || sharedPreferences.getBoolean(getString(R.string.clear_cookies_key), true)) { // Request the cookies be deleted. CookieManager.getInstance().removeAllCookies(null); @@ -4778,7 +4743,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Clear DOM storage. - if (clearEverything || sharedPreferences.getBoolean("clear_dom_storage", true)) { + if (clearEverything || sharedPreferences.getBoolean(getString(R.string.clear_dom_storage_key), true)) { // Ask `WebStorage` to clear the DOM storage. WebStorage webStorage = WebStorage.getInstance(); webStorage.deleteAllData(); @@ -4806,7 +4771,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Clear form data if the API < 26. - if ((Build.VERSION.SDK_INT < 26) && (clearEverything || sharedPreferences.getBoolean("clear_form_data", true))) { + if ((Build.VERSION.SDK_INT < 26) && (clearEverything || sharedPreferences.getBoolean(getString(R.string.clear_form_data_key), true))) { WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(this); webViewDatabase.clearFormData(); @@ -4838,7 +4803,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Clear the cache. - if (clearEverything || sharedPreferences.getBoolean("clear_cache", true)) { + if (clearEverything || sharedPreferences.getBoolean(getString(R.string.clear_cache_key), true)) { // Clear the cache from each WebView. for (int i = 0; i < webViewPagerAdapter.getCount(); i++) { // Get the WebView tab fragment. @@ -4864,7 +4829,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Delete the secondary `Service Worker` cache directory. // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly. - Process deleteServiceWorkerProcess = runtime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"}); + Process deleteServiceWorkerProcess = runtime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Default/Service Worker/"}); // Wait until the processes have finished. deleteCacheProcess.waitFor(); @@ -4901,9 +4866,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) { @@ -5034,44 +4996,44 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @SuppressLint("ClickableViewAccessibility") @Override - public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url, Boolean restoringState) { + public void initializeWebView(@NonNull NestedScrollWebView nestedScrollWebView, int pageNumber, @NonNull ProgressBar progressBar, @NonNull String url, boolean restoringState) { // Get a handle for the shared preferences. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); // Get the WebView theme. - String webViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value)); + String webViewTheme = sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value)); // Get the WebView theme entry values string array. String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values); - // Apply the WebView theme if supported by the installed WebView. - if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { - // Set the WebView theme. A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant. + // Set the WebView theme if device is running API >= 29 and algorithmic darkening is supported. + if ((Build.VERSION.SDK_INT >= 29) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) { + // Set the WebView them. A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant. if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) { // The light theme is selected. - // Turn off the WebView dark mode. - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); + // Turn off algorithmic darkening. + WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), false); // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode. // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`. nestedScrollWebView.setVisibility(View.VISIBLE); } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected. - // Turn on the WebView dark mode. - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); - } else { // The system default theme is selected. - // Get the current system theme status. + // Turn on algorithmic darkening. + WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), true); + } else { + // The system default theme is selected. int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - // Set the WebView theme according to the current system theme status. + // Set the algorithmic darkening according to the current system theme status. if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode. - // Turn off the WebView dark mode. - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); + // Turn off algorithmic darkening. + WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), false); // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode. // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`. nestedScrollWebView.setVisibility(View.VISIBLE); } else { // The system is in night mode. - // Turn on the WebView dark mode. - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); + // Turn on algorithmic darkening. + WebSettingsCompat.setAlgorithmicDarkeningAllowed(nestedScrollWebView.getSettings(), true); } } } @@ -5121,7 +5083,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 +5093,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 +5113,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 +5143,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 +5182,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 50 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 +5279,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 +5290,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 +5902,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 +5925,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); @@ -5965,13 +5968,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the title. optionsRefreshMenuItem.setTitle(R.string.stop); - // Get the app bar and theme preferences. - boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean(getString(R.string.display_additional_app_bar_icons_key), false); - - // 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) { + // Set the icon if it is displayed in the AppBar. + if (displayAdditionalAppBarIcons) optionsRefreshMenuItem.setIcon(R.drawable.close_blue); - } } } @@ -5987,16 +5986,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Reset the Refresh title. optionsRefreshMenuItem.setTitle(R.string.refresh); - // 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 app bar, reset it according to the theme. - if (displayAdditionalAppBarIcons) { - // Set the icon. + // Reset the icon if it is displayed in the app bar. + if (displayAdditionalAppBarIcons) optionsRefreshMenuItem.setIcon(R.drawable.refresh_enabled); - } } + // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`, + // which links to `/data/data/com.stoutner.privacybrowser.standard`. + String privateDataDirectoryString = getApplicationInfo().dataDir; + // Clear the cache, history, and logcat if Incognito Mode is enabled. if (incognitoModeEnabled) { // Clear the cache. `true` includes disk files. @@ -6007,16 +6005,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Manually delete cache folders. try { - // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`, - // which links to `/data/data/com.stoutner.privacybrowser.standard`. - String privateDataDirectoryString = getApplicationInfo().dataDir; - // Delete the main cache directory. Runtime.getRuntime().exec("rm -rf " + privateDataDirectoryString + "/cache"); - - // Delete the secondary `Service Worker` cache directory. - // A `String[]` must be used because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise. - Runtime.getRuntime().exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"}); } catch (IOException exception) { // Do nothing if an error is thrown. } @@ -6030,6 +6020,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } + // Clear the `Service Worker` directory. + try { + // A `String[]` must be used because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise. + Runtime.getRuntime().exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Default/Service Worker/"}); + } catch (IOException exception) { + // Do nothing. + } + // Get the current page position. int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId()); @@ -6239,4 +6237,4 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } } -} \ No newline at end of file +}