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=b00e19a7e05c24d5c3e7bf0341c2a2cfdf6d19b7;hp=88d418677a12c3e2b4299a0433837451a5fe8e0b;hb=4c00dd29a0a8592dff337d8b01870f06134c070b;hpb=d4f39c36beb5e6c3568a1e075274ad66defd8e8e 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 88d41867..b00e19a7 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -48,11 +48,13 @@ import android.net.http.SslError; import android.os.AsyncTask; import android.os.Build; 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; import android.text.Editable; import android.text.Spanned; import android.text.TextWatcher; @@ -82,6 +84,7 @@ import android.webkit.WebView; import android.webkit.WebViewClient; import android.webkit.WebViewDatabase; import android.widget.ArrayAdapter; +import android.widget.CheckBox; import android.widget.CursorAdapter; import android.widget.EditText; import android.widget.FrameLayout; @@ -149,7 +152,11 @@ import com.stoutner.privacybrowser.views.NestedScrollWebView; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; @@ -334,8 +341,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private MenuItem navigationRequestsMenuItem; private MenuItem optionsPrivacyMenuItem; private MenuItem optionsRefreshMenuItem; - private MenuItem optionsFirstPartyCookiesMenuItem; - private MenuItem optionsThirdPartyCookiesMenuItem; + private MenuItem optionsCookiesMenuItem; private MenuItem optionsDomStorageMenuItem; private MenuItem optionsSaveFormDataMenuItem; private MenuItem optionsClearDataMenuItem; @@ -815,8 +821,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get handles for the class menu items. optionsPrivacyMenuItem = menu.findItem(R.id.javascript); optionsRefreshMenuItem = menu.findItem(R.id.refresh); - optionsFirstPartyCookiesMenuItem = menu.findItem(R.id.first_party_cookies); - optionsThirdPartyCookiesMenuItem = menu.findItem(R.id.third_party_cookies); + optionsCookiesMenuItem = menu.findItem(R.id.cookies); optionsDomStorageMenuItem = menu.findItem(R.id.dom_storage); optionsSaveFormDataMenuItem = menu.findItem(R.id.save_form_data); // Form data can be removed once the minimum API >= 26. optionsClearDataMenuItem = menu.findItem(R.id.clear_data); @@ -864,9 +869,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the initial status of the privacy icons. `false` does not call `invalidateOptionsMenu` as the last step. updatePrivacyIcons(false); - // Only display third-party cookies if API >= 21 - optionsThirdPartyCookiesMenuItem.setVisible(Build.VERSION.SDK_INT >= 21); - // Only display the form data menu items if the API < 26. optionsSaveFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26); optionsClearFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26); @@ -890,11 +892,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook if (displayAdditionalAppBarIcons) { optionsRefreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); bookmarksMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - optionsFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + optionsCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); } else { //Do not display the additional icons. optionsRefreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); bookmarksMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - optionsFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); + optionsCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); } // Replace Refresh with Stop if a URL is already loading. @@ -968,15 +970,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook optionsUltraPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.ULTRAPRIVACY) + " - " + getString(R.string.ultraprivacy)); optionsBlockAllThirdPartyRequestsMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.THIRD_PARTY_REQUESTS) + " - " + getString(R.string.block_all_third_party_requests)); - // Only modify third-party cookies if the API >= 21. - if (Build.VERSION.SDK_INT >= 21) { - // Set the status of the third-party cookies checkbox. - optionsThirdPartyCookiesMenuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView)); - - // Enable third-party cookies if first-party cookies are enabled. - optionsThirdPartyCookiesMenuItem.setEnabled(cookieManager.acceptCookie()); - } - // Enable DOM Storage if JavaScript is enabled. optionsDomStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled()); @@ -986,8 +979,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - // Set the checked status of the first party cookies menu item. - optionsFirstPartyCookiesMenuItem.setChecked(cookieManager.acceptCookie()); + // Set the cookies menu item checked status. + optionsCookiesMenuItem.setChecked(cookieManager.acceptCookie()); // Enable Clear Cookies if there are any. optionsClearCookiesMenuItem.setEnabled(cookieManager.hasCookies()); @@ -1206,12 +1199,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Consume the event. return true; - } else if (menuItemId == R.id.first_party_cookies) { // First-party cookies. + } else if (menuItemId == R.id.cookies) { // Cookies. // Switch the first-party cookie status. cookieManager.setAcceptCookie(!cookieManager.acceptCookie()); - // Store the first-party cookie status. - currentWebView.setAcceptFirstPartyCookies(cookieManager.acceptCookie()); + // Store the cookie status. + currentWebView.setAcceptCookies(cookieManager.acceptCookie()); // Update the menu checkbox. menuItem.setChecked(cookieManager.acceptCookie()); @@ -1220,10 +1213,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook updatePrivacyIcons(true); // Display a snackbar. - if (cookieManager.acceptCookie()) { // First-party cookies are enabled. - Snackbar.make(webViewPager, R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show(); + if (cookieManager.acceptCookie()) { // Cookies are enabled. + Snackbar.make(webViewPager, R.string.cookies_enabled, Snackbar.LENGTH_SHORT).show(); } else if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is still enabled. - Snackbar.make(webViewPager, R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(webViewPager, R.string.cookies_disabled, Snackbar.LENGTH_SHORT).show(); } else { // Privacy mode. Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show(); } @@ -1231,28 +1224,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Reload the current WebView. currentWebView.reload(); - // Consume the event. - return true; - } else if (menuItemId == R.id.third_party_cookies) { // Third-party cookies. - // Only act if the API >= 21. Otherwise, there are no third-party cookie controls. - if (Build.VERSION.SDK_INT >= 21) { - // Toggle the status of thirdPartyCookiesEnabled. - cookieManager.setAcceptThirdPartyCookies(currentWebView, !cookieManager.acceptThirdPartyCookies(currentWebView)); - - // Update the menu checkbox. - menuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView)); - - // Display a snackbar. - if (cookieManager.acceptThirdPartyCookies(currentWebView)) { - Snackbar.make(webViewPager, R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show(); - } else { - Snackbar.make(webViewPager, R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show(); - } - - // Reload the current WebView. - currentWebView.reload(); - } - // Consume the event. return true; } else if (menuItemId == R.id.dom_storage) { // DOM storage. @@ -1751,13 +1722,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } else if (menuItemId == R.id.save_url) { // Save URL. // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), - currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl()); + currentWebView.getAcceptCookies()).execute(currentWebView.getCurrentUrl()); // Consume the event. + return true; + } else if (menuItemId == R.id.save_archive) { + // Instantiate the save dialog. + DialogFragment saveArchiveFragment = SaveWebpageDialog.saveWebpage(SaveWebpageDialog.SAVE_ARCHIVE, currentWebView.getCurrentUrl(), null, null, null, + false); + + // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name. + saveArchiveFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); + return true; } else if (menuItemId == R.id.save_image) { // Save image. // Instantiate the save dialog. - DialogFragment saveImageFragment = SaveWebpageDialog.saveWebpage(SaveWebpageDialog.SAVE_IMAGE, null, null, getString(R.string.webpage_png), null, + DialogFragment saveImageFragment = SaveWebpageDialog.saveWebpage(SaveWebpageDialog.SAVE_IMAGE, currentWebView.getCurrentUrl(), null, null, null, false); // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name. @@ -2015,14 +1995,50 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Make it so. startActivity(requestsIntent); } else if (menuItemId == R.id.downloads) { // Downloads. - // Launch the system Download Manager. - Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS); + // Try the default system download manager. + try { + // Launch the default system Download Manager. + Intent defaultDownloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS); - // Launch as a new task so that Download Manager and Privacy Browser show as separate windows in the recent tasks list. - downloadManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // Launch as a new task so that the download manager and Privacy Browser show as separate windows in the recent tasks list. + defaultDownloadManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - // Make it so. - startActivity(downloadManagerIntent); + // Make it so. + startActivity(defaultDownloadManagerIntent); + } catch (Exception defaultDownloadManagerException) { + // Try a generic file manager. + try { + // Create a generic file manager intent. + Intent genericFileManagerIntent = new Intent(Intent.ACTION_VIEW); + + // Open the download directory. + genericFileManagerIntent.setDataAndType(Uri.parse(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString()), DocumentsContract.Document.MIME_TYPE_DIR); + + // Launch as a new task so that the file manager and Privacy Browser show as separate windows in the recent tasks list. + genericFileManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // Make it so. + startActivity(genericFileManagerIntent); + } catch (Exception genericFileManagerException) { + // Try an alternate file manager. + try { + // Create an alternate file manager intent. + Intent alternateFileManagerIntent = new Intent(Intent.ACTION_VIEW); + + // Open the download directory. + alternateFileManagerIntent.setDataAndType(Uri.parse(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString()), "resource/folder"); + + // Launch as a new task so that the file manager and Privacy Browser show as separate windows in the recent tasks list. + alternateFileManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // Open the alternate file manager. + startActivity(alternateFileManagerIntent); + } catch (Exception alternateFileManagerException) { + // Display a snackbar. + Snackbar.make(currentWebView, R.string.no_file_manager_detected, Snackbar.LENGTH_INDEFINITE).show(); + } + } + } } else if (menuItemId == R.id.domains) { // Domains. // Set the flag to reapply the domain settings on restart when returning from Domain Settings. reapplyDomainSettingsOnRestart = true; @@ -2219,7 +2235,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook menu.add(R.string.save_url).setOnMenuItemClickListener((MenuItem item) -> { // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), - currentWebView.getAcceptFirstPartyCookies()).execute(linkUrl); + currentWebView.getAcceptCookies()).execute(linkUrl); // Consume the event. return true; @@ -2286,7 +2302,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook menu.add(R.string.save_image).setOnMenuItemClickListener((MenuItem item) -> { // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), - currentWebView.getAcceptFirstPartyCookies()).execute(imageUrl); + currentWebView.getAcceptCookies()).execute(imageUrl); // Consume the event. return true; @@ -2386,7 +2402,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook menu.add(R.string.save_image).setOnMenuItemClickListener((MenuItem item) -> { // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), - currentWebView.getAcceptFirstPartyCookies()).execute(imageUrl); + currentWebView.getAcceptCookies()).execute(imageUrl); // Consume the event. return true; @@ -2408,7 +2424,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook menu.add(R.string.save_url).setOnMenuItemClickListener((MenuItem item) -> { // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), - currentWebView.getAcceptFirstPartyCookies()).execute(linkUrl); + currentWebView.getAcceptCookies()).execute(linkUrl); // Consume the event. return true; @@ -2994,8 +3010,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Remove the incorrect lint warning below that the dialog might be null. assert dialog != null; - // Get a handle for the file name edit text. + // Get handles for the views. EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext); + CheckBox mhtCheckBox = dialog.findViewById(R.id.mht_checkbox); // Get the file path string. String openFilePath = fileNameEditText.getText().toString(); @@ -3003,42 +3020,133 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Apply the domain settings. This resets the favorite icon and removes any domain settings. applyDomainSettings(currentWebView, openFilePath, true, false, false); - // Open the file. - currentWebView.loadUrl(openFilePath); + // Open the file according to the type. + if (mhtCheckBox.isChecked()) { // Force opening of an MHT file. + try { + // Get the MHT file input stream. + InputStream mhtFileInputStream = getContentResolver().openInputStream(Uri.parse(openFilePath)); + + // Create a temporary MHT file. + File temporaryMhtFile = File.createTempFile("temporary_mht_file", ".mht", getCacheDir()); + + // Get a file output stream for the temporary MHT file. + FileOutputStream temporaryMhtFileOutputStream = new FileOutputStream(temporaryMhtFile); + + // Create a transfer byte array. + byte[] transferByteArray = new byte[1024]; + + // Create an integer to track the number of bytes read. + int bytesRead; + + // Copy the temporary MHT file input stream to the MHT output stream. + while ((bytesRead = mhtFileInputStream.read(transferByteArray)) > 0) { + temporaryMhtFileOutputStream.write(transferByteArray, 0, bytesRead); + } + + // Flush the temporary MHT file output stream. + temporaryMhtFileOutputStream.flush(); + + // Close the streams. + temporaryMhtFileOutputStream.close(); + mhtFileInputStream.close(); + + // Load the temporary MHT file. + currentWebView.loadUrl(temporaryMhtFile.toString()); + } catch (Exception exception) { + // Display a snackbar. + Snackbar.make(currentWebView, getString(R.string.error) + " " + exception.toString(), Snackbar.LENGTH_INDEFINITE).show(); + } + } else { // Let the WebView handle opening of the file. + // Open the file. + currentWebView.loadUrl(openFilePath); + } } @Override - public void onSaveWebpage(int saveType, String originalUrlString, DialogFragment dialogFragment) { + public void onSaveWebpage(int saveType, @NonNull String originalUrlString, DialogFragment dialogFragment) { // Get the dialog. Dialog dialog = dialogFragment.getDialog(); // Remove the incorrect lint warning below that the dialog might be null. assert dialog != null; - // Get a handle for the edit texts. - EditText dialogUrlEditText = dialog.findViewById(R.id.url_edittext); + // Get a handle for the file name edit text. EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext); - // Define the save webpage URL. - String saveWebpageUrl; - - // Store the URL. - if ((originalUrlString != null) && originalUrlString.startsWith("data:")) { - // Save the original URL. - saveWebpageUrl = originalUrlString; - } else { - // Get the URL from the edit text, which may have been modified. - saveWebpageUrl = dialogUrlEditText.getText().toString(); - } - // Get the file path from the edit text. String saveWebpageFilePath = fileNameEditText.getText().toString(); //Save the webpage according to the save type. switch (saveType) { case SaveWebpageDialog.SAVE_URL: + // Get a handle for the dialog URL edit text. + EditText dialogUrlEditText = dialog.findViewById(R.id.url_edittext); + + // Define the save webpage URL. + String saveWebpageUrl; + + // Store the URL. + if (originalUrlString.startsWith("data:")) { + // Save the original URL. + saveWebpageUrl = originalUrlString; + } else { + // Get the URL from the edit text, which may have been modified. + saveWebpageUrl = dialogUrlEditText.getText().toString(); + } + // Save the URL. - new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl); + new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptCookies()).execute(saveWebpageUrl); + break; + + case SaveWebpageDialog.SAVE_ARCHIVE: + 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 { + // Create a temporary MHT file input stream. + FileInputStream temporaryMhtFileInputStream = new FileInputStream(temporaryMhtFile); + + // Get an output stream for the save webpage file path. + OutputStream mhtOutputStream = getContentResolver().openOutputStream(Uri.parse(saveWebpageFilePath)); + + // Create a transfer byte array. + byte[] transferByteArray = new byte[1024]; + + // Create an integer to track the number of bytes read. + int bytesRead; + + // Copy the temporary MHT file input stream to the MHT output stream. + while ((bytesRead = temporaryMhtFileInputStream.read(transferByteArray)) > 0) { + mhtOutputStream.write(transferByteArray, 0, bytesRead); + } + + // Close the streams. + mhtOutputStream.close(); + temporaryMhtFileInputStream.close(); + + // Display a snackbar. + Snackbar.make(currentWebView, getString(R.string.file_saved) + " " + currentWebView.getCurrentUrl(), 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(); + } finally { + // Delete the temporary MHT file. + //noinspection ResultOfMethodCallIgnored + temporaryMhtFile.delete(); + } + } 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(); + } + }); + } 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(); + } break; case SaveWebpageDialog.SAVE_IMAGE: @@ -3461,7 +3569,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Store the values from the shared preferences in variables. incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false); - boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false); sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true); sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true); sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true); @@ -3492,13 +3599,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Apply the proxy. applyProxy(false); - // Set Do Not Track status. - if (doNotTrackEnabled) { - customHeaders.put("DNT", "1"); - } else { - customHeaders.remove("DNT"); - } - // 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(); @@ -3607,7 +3707,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void navigateHistory(String url, int steps) { + public void navigateHistory(@NonNull String url, int steps) { // Apply the domain settings. applyDomainSettings(currentWebView, url, false, false, false); @@ -3773,8 +3873,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the settings from the cursor. nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID))); nestedScrollWebView.getSettings().setJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1); - nestedScrollWebView.setAcceptFirstPartyCookies(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1); - boolean domainThirdPartyCookiesEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1); + nestedScrollWebView.setAcceptCookies(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.COOKIES)) == 1); nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1); // Form data can be removed once the minimum API >= 26. boolean saveFormData = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1); @@ -3844,12 +3943,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Apply the cookie domain settings. - cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies()); - - // Set third-party cookies status if API >= 21. - if (Build.VERSION.SDK_INT >= 21) { - cookieManager.setAcceptThirdPartyCookies(nestedScrollWebView, domainThirdPartyCookiesEnabled); - } + cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptCookies()); // Apply the form data setting if the API < 26. if (Build.VERSION.SDK_INT < 26) { @@ -4028,8 +4122,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } 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.setAcceptFirstPartyCookies(sharedPreferences.getBoolean("first_party_cookies", false)); - boolean defaultThirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", 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.enableBlocklist(NestedScrollWebView.EASYLIST, sharedPreferences.getBoolean("easylist", true)); @@ -4040,8 +4133,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, sharedPreferences.getBoolean("ultraprivacy", true)); nestedScrollWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, sharedPreferences.getBoolean("block_all_third_party_requests", false)); - // Apply the default first-party cookie setting. - cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies()); + // Apply the default cookie setting. + cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptCookies()); // Apply the default font size setting. try { @@ -4072,11 +4165,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Reset the pinned variables. nestedScrollWebView.setDomainSettingsDatabaseId(-1); - // Set third-party cookies status if API >= 21. - if (Build.VERSION.SDK_INT >= 21) { - cookieManager.setAcceptThirdPartyCookies(nestedScrollWebView, defaultThirdPartyCookiesEnabled); - } - // Get the array position of the user agent name. int userAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName); @@ -4287,7 +4375,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the privacy icon. if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is enabled. optionsPrivacyMenuItem.setIcon(R.drawable.javascript_enabled); - } else if (currentWebView.getAcceptFirstPartyCookies()) { // JavaScript is disabled but cookies are enabled. + } else if (currentWebView.getAcceptCookies()) { // JavaScript is disabled but cookies are enabled. optionsPrivacyMenuItem.setIcon(R.drawable.warning); } else { // All the dangerous features are disabled. optionsPrivacyMenuItem.setIcon(R.drawable.privacy_mode); @@ -4296,14 +4384,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the current theme status. int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - // Update the first-party cookies icon. - if (currentWebView.getAcceptFirstPartyCookies()) { // First-party cookies are enabled. - optionsFirstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled); - } else { // First-party cookies are disabled. + // Update the cookies icon. + if (currentWebView.getAcceptCookies()) { // Cookies are enabled. + optionsCookiesMenuItem.setIcon(R.drawable.cookies_enabled); + } else { // Cookies are disabled. if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - optionsFirstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_day); + optionsCookiesMenuItem.setIcon(R.drawable.cookies_disabled_day); } else { - optionsFirstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night); + optionsCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night); } } @@ -4924,8 +5012,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get a handle for the cookie manager. CookieManager cookieManager = CookieManager.getInstance(); - // Set the first-party cookie status. - cookieManager.setAcceptCookie(currentWebView.getAcceptFirstPartyCookies()); + // Set the cookie status. + cookieManager.setAcceptCookie(currentWebView.getAcceptCookies()); // Update the privacy icons. `true` redraws the icons in the app bar. updatePrivacyIcons(true); @@ -5075,6 +5163,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Explicitly disable geolocation. nestedScrollWebView.getSettings().setGeolocationEnabled(false); + // Allow loading of file:// URLs. This is necessary for opening MHT web archives, which are copies into a temporary cache location. + nestedScrollWebView.getSettings().setAllowFileAccess(true); + // Create a double-tap gesture detector to toggle full-screen mode. GestureDetector doubleTapGestureDetector = new GestureDetector(getApplicationContext(), new GestureDetector.SimpleOnGestureListener() { // Override `onDoubleTap()`. All other events are handled using the default settings. @@ -5218,7 +5309,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Instantiate the save dialog. DialogFragment saveDialogFragment = SaveWebpageDialog.saveWebpage(SaveWebpageDialog.SAVE_URL, downloadUrl, formattedFileSizeString, fileNameString, userAgent, - nestedScrollWebView.getAcceptFirstPartyCookies()); + nestedScrollWebView.getAcceptCookies()); // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name. saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); @@ -6048,7 +6139,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.getAcceptFirstPartyCookies() && Build.VERSION.SDK_INT >= 21) { + if (nestedScrollWebView.getAcceptCookies() && Build.VERSION.SDK_INT >= 21) { CookieManager.getInstance().flush(); }