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=0fd451611a28312b03479e36194e921e89c08878;hp=d08f7c63c625a2c45566061891ad9167b954de7a;hb=e9c77e79c3c6f7612f051b7c111e029ad125817b;hpb=cd609c9888924549c03cd6509ed889cdb052d5bb 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 d08f7c63..0fd45161 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -55,6 +55,7 @@ import android.preference.PreferenceManager; import android.print.PrintDocumentAdapter; import android.print.PrintManager; import android.provider.DocumentsContract; +import android.provider.OpenableColumns; import android.text.Editable; import android.text.Spanned; import android.text.TextWatcher; @@ -96,6 +97,9 @@ import android.widget.RadioButton; import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.activity.result.ActivityResultCallback; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; @@ -107,6 +111,7 @@ import androidx.core.content.res.ResourcesCompat; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.viewpager.widget.ViewPager; import androidx.webkit.WebSettingsCompat; @@ -126,6 +131,7 @@ import com.stoutner.privacybrowser.asynctasks.PopulateBlocklists; import com.stoutner.privacybrowser.asynctasks.PrepareSaveDialog; import com.stoutner.privacybrowser.asynctasks.SaveUrl; import com.stoutner.privacybrowser.asynctasks.SaveWebpageImage; +import com.stoutner.privacybrowser.dataclasses.PendingDialog; import com.stoutner.privacybrowser.dialogs.AdConsentDialog; import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog; import com.stoutner.privacybrowser.dialogs.CreateBookmarkFolderDialog; @@ -136,7 +142,7 @@ import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog; import com.stoutner.privacybrowser.dialogs.OpenDialog; import com.stoutner.privacybrowser.dialogs.ProxyNotInstalledDialog; import com.stoutner.privacybrowser.dialogs.PinnedMismatchDialog; -import com.stoutner.privacybrowser.dialogs.SaveWebpageDialog; +import com.stoutner.privacybrowser.dialogs.SaveDialog; import com.stoutner.privacybrowser.dialogs.SslCertificateErrorDialog; import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog; import com.stoutner.privacybrowser.dialogs.ViewSslCertificateDialog; @@ -180,7 +186,7 @@ import java.util.concurrent.Executors; public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, OpenDialog.OpenListener, - PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveWebpageDialog.SaveWebpageListener, UrlHistoryDialog.NavigateHistoryListener, + PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveDialog.SaveListener, UrlHistoryDialog.NavigateHistoryListener, WebViewTabFragment.NewTabListener { // The executor service handles background tasks. It is accessed from `ViewSourceActivity`. @@ -195,6 +201,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `restartFromBookmarksActivity` is public static so it can be accessed from `BookmarksActivity`. It is also used in `onRestart()`. public static boolean restartFromBookmarksActivity; + // Define the public static variables. + public static ArrayList pendingDialogsArrayList = new ArrayList<>(); + // `currentBookmarksFolder` is public static so it can be accessed from `BookmarksActivity`. It is also used in `onCreate()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, // `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`. public static String currentBookmarksFolder; @@ -207,10 +216,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook public final static int DOMAINS_WEBVIEW_DEFAULT_USER_AGENT = 2; public final static int DOMAINS_CUSTOM_USER_AGENT = 13; - // Define the start activity for result request codes. The public static entries are accessed from `OpenDialog()` and `SaveWebpageDialog()`. + // Define the start activity for result request codes. The public static entry is accessed from `OpenDialog()`. private final int BROWSE_FILE_UPLOAD_REQUEST_CODE = 0; public final static int BROWSE_OPEN_REQUEST_CODE = 1; - public final static int BROWSE_SAVE_WEBPAGE_REQUEST_CODE = 2; // The proxy mode is public static so it can be accessed from `ProxyHelper()`. // It is also used in `onRestart()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `applyAppSettings()`, and `applyProxy()`. @@ -251,22 +259,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private ArrayList> ultraList; private ArrayList> ultraPrivacy; - // Declare the class variables - private BroadcastReceiver orbotStatusBroadcastReceiver; - private String webViewDefaultUserAgent; - private boolean incognitoModeEnabled; - private boolean fullScreenBrowsingModeEnabled; - private boolean inFullScreenBrowsingMode; - private boolean downloadWithExternalApp; - private boolean hideAppBar; - private boolean scrollAppBar; - private boolean bottomAppBar; - private boolean loadingNewIntent; - private boolean reapplyDomainSettingsOnRestart; - private boolean reapplyAppSettingsOnRestart; - private boolean displayingFullScreenVideo; - private boolean waitingForProxy; - // The action bar drawer toggle is initialized in `onCreate()` and used in `onResume()`. private ActionBarDrawerToggle actionBarDrawerToggle; @@ -301,8 +293,25 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private boolean sanitizeFacebookClickIds; private boolean sanitizeTwitterAmpRedirects; + // Declare the class variables + private BroadcastReceiver orbotStatusBroadcastReceiver; + private String webViewDefaultUserAgent; + private boolean incognitoModeEnabled; + private boolean fullScreenBrowsingModeEnabled; + private boolean inFullScreenBrowsingMode; + private boolean downloadWithExternalApp; + private boolean hideAppBar; + private boolean scrollAppBar; + private boolean bottomAppBar; + private boolean loadingNewIntent; + private boolean reapplyDomainSettingsOnRestart; + private boolean reapplyAppSettingsOnRestart; + private boolean displayingFullScreenVideo; + private boolean waitingForProxy; + // Define the class variables. private long lastScrollUpdate = 0; + private String saveUrlString = ""; // Declare the class views. private FrameLayout rootFrameLayout; @@ -371,13 +380,124 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private MenuItem optionsFontSizeMenuItem; private MenuItem optionsAddOrEditDomainMenuItem; - @Override - // Remove the warning about needing to override `performClick()` when using an `OnTouchListener` with `WebView`. + // This variable won't be needed once the class is migrated to Kotlin, as can be seen in LogcatActivity or AboutVersionFragment. + 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(), + new ActivityResultCallback() { + @Override + public void onActivityResult(Uri fileUri) { + // Only save the URL if the file URI is not null, which happens if the user exited the file picker by pressing back. + if (fileUri != null) { + new SaveUrl(getApplicationContext(), resultLauncherActivityHandle, fileUri, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptCookies()).execute(saveUrlString); + } + + // Reset the save URL string. + saveUrlString = ""; + } + }); + + // 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(), + 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) { + 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(fileUri); + + // 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(); + + // 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.getColumnIndex(OpenableColumns.DISPLAY_NAME)); + + // Close the cursor. + contentResolverCursor.close(); + } + + // Display a snackbar. + Snackbar.make(currentWebView, getString(R.string.file_saved) + " " + fileNameString, Snackbar.LENGTH_SHORT).show(); + } catch (Exception exception) { + // Display a snackbar with the exception. + Snackbar.make(currentWebView, getString(R.string.error_saving_file) + " " + exception.toString(), Snackbar.LENGTH_INDEFINITE).show(); + } 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(); + } + } + } + }); + + // 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(), + new ActivityResultCallback() { + @Override + public void onActivityResult(Uri fileUri) { + // Only save the webpage image if the file URI is not null, which happens if the user exited the file picker by pressing back. + if (fileUri != null) { + // Save the webpage image. + new SaveWebpageImage(resultLauncherActivityHandle, fileUri, currentWebView).execute(); + } + } + }); + + // Remove the warning about needing to override `performClick()` when using an `OnTouchListener` with WebView. @SuppressLint("ClickableViewAccessibility") + @Override protected void onCreate(Bundle savedInstanceState) { // Run the default commands. super.onCreate(savedInstanceState); + // Populate the result launcher activity. This will no longer be needed once the activity has transitioned to Kotlin. + resultLauncherActivityHandle = this; + // Check to see if the activity has been restarted. if (savedInstanceState != null) { // Store the saved instance state variables. @@ -449,10 +569,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook tabLayout = findViewById(R.id.tablayout); swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); webViewPager = findViewById(R.id.webviewpager); - fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout); - - // Get a handle for the navigation view. NavigationView navigationView = findViewById(R.id.navigationview); + fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout); // Get a handle for the navigation menu. Menu navigationMenu = navigationView.getMenu(); @@ -626,7 +744,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview); // Reset the current domain name so the domain settings will be reapplied. - nestedScrollWebView.resetCurrentDomainName(); + nestedScrollWebView.setCurrentDomainName(""); // Reapply the domain settings if the URL is not null, which can happen if an empty tab is active when returning from settings. if (nestedScrollWebView.getUrl() != null) { @@ -703,6 +821,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Resume the ad. AdHelper.resumeAd(adView); } + + // Show any pending dialogs. + for (int i = 0; i < pendingDialogsArrayList.size(); i++) { + // Get the pending dialog from the array list. + PendingDialog pendingDialog = pendingDialogsArrayList.get(i); + + // Show the pending dialog. + pendingDialog.dialogFragment.show(getSupportFragmentManager(), pendingDialog.tag); + } + + // Clear the pending dialogs array list. + pendingDialogsArrayList.clear(); } // `onStop()` runs after `onPause()`. It is used instead of `onPause()` so the commands are not called every time the screen is partially hidden. @@ -952,13 +1082,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the status of the menu item checkboxes. optionsDomStorageMenuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled()); optionsSaveFormDataMenuItem.setChecked(currentWebView.getSettings().getSaveFormData()); // Form data can be removed once the minimum API >= 26. - optionsEasyListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST)); - optionsEasyPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY)); - optionsFanboysAnnoyanceListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); - optionsFanboysSocialBlockingListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST)); - optionsUltraListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST)); - optionsUltraPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY)); - optionsBlockAllThirdPartyRequestsMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)); + optionsEasyListMenuItem.setChecked(currentWebView.getEasyListEnabled()); + optionsEasyPrivacyMenuItem.setChecked(currentWebView.getEasyPrivacyEnabled()); + optionsFanboysAnnoyanceListMenuItem.setChecked(currentWebView.getFanboysAnnoyanceListEnabled()); + optionsFanboysSocialBlockingListMenuItem.setChecked(currentWebView.getFanboysSocialBlockingListEnabled()); + optionsUltraListMenuItem.setChecked(currentWebView.getUltraListEnabled()); + optionsUltraPrivacyMenuItem.setChecked(currentWebView.getUltraPrivacyEnabled()); + optionsBlockAllThirdPartyRequestsMenuItem.setChecked(currentWebView.getBlockAllThirdPartyRequests()); optionsSwipeToRefreshMenuItem.setChecked(currentWebView.getSwipeToRefresh()); optionsWideViewportMenuItem.setChecked(currentWebView.getSettings().getUseWideViewPort()); optionsDisplayImagesMenuItem.setChecked(currentWebView.getSettings().getLoadsImagesAutomatically()); @@ -1376,10 +1506,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; } else if (menuItemId == R.id.easylist) { // EasyList. // Toggle the EasyList status. - currentWebView.enableBlocklist(NestedScrollWebView.EASYLIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST)); + currentWebView.setEasyListEnabled(!currentWebView.getEasyListEnabled()); // Update the menu checkbox. - menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST)); + menuItem.setChecked(currentWebView.getEasyListEnabled()); // Reload the current WebView. currentWebView.reload(); @@ -1388,10 +1518,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; } else if (menuItemId == R.id.easyprivacy) { // EasyPrivacy. // Toggle the EasyPrivacy status. - currentWebView.enableBlocklist(NestedScrollWebView.EASYPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY)); + currentWebView.setEasyPrivacyEnabled(!currentWebView.getEasyPrivacyEnabled()); // Update the menu checkbox. - menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY)); + menuItem.setChecked(currentWebView.getEasyPrivacyEnabled()); // Reload the current WebView. currentWebView.reload(); @@ -1400,13 +1530,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; } else if (menuItemId == R.id.fanboys_annoyance_list) { // Fanboy's Annoyance List. // Toggle Fanboy's Annoyance List status. - currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); + currentWebView.setFanboysAnnoyanceListEnabled(!currentWebView.getFanboysAnnoyanceListEnabled()); // Update the menu checkbox. - menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); + menuItem.setChecked(currentWebView.getFanboysAnnoyanceListEnabled()); // Update the status of Fanboy's Social Blocking List. - optionsFanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); + optionsFanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.getFanboysAnnoyanceListEnabled()); // Reload the current WebView. currentWebView.reload(); @@ -1415,10 +1545,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; } else if (menuItemId == R.id.fanboys_social_blocking_list) { // Fanboy's Social Blocking List. // Toggle Fanboy's Social Blocking List status. - currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST)); + currentWebView.setFanboysSocialBlockingListEnabled(!currentWebView.getFanboysSocialBlockingListEnabled()); // Update the menu checkbox. - menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST)); + menuItem.setChecked(currentWebView.getFanboysSocialBlockingListEnabled()); // Reload the current WebView. currentWebView.reload(); @@ -1427,10 +1557,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; } else if (menuItemId == R.id.ultralist) { // UltraList. // Toggle the UltraList status. - currentWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST)); + currentWebView.setUltraListEnabled(!currentWebView.getUltraListEnabled()); // Update the menu checkbox. - menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST)); + menuItem.setChecked(currentWebView.getUltraListEnabled()); // Reload the current WebView. currentWebView.reload(); @@ -1439,10 +1569,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; } else if (menuItemId == R.id.ultraprivacy) { // UltraPrivacy. // Toggle the UltraPrivacy status. - currentWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY)); + currentWebView.setUltraPrivacyEnabled(!currentWebView.getUltraPrivacyEnabled()); // Update the menu checkbox. - menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY)); + menuItem.setChecked(currentWebView.getUltraPrivacyEnabled()); // Reload the current WebView. currentWebView.reload(); @@ -1451,10 +1581,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; } else if (menuItemId == R.id.block_all_third_party_requests) { // Block all third-party requests. //Toggle the third-party requests blocker status. - currentWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, !currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)); + currentWebView.setBlockAllThirdPartyRequests(!currentWebView.getBlockAllThirdPartyRequests()); // Update the menu checkbox. - menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)); + menuItem.setChecked(currentWebView.getBlockAllThirdPartyRequests()); // Reload the current WebView. currentWebView.reload(); @@ -1728,28 +1858,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook downloadUrlWithExternalApp(currentWebView.getCurrentUrl()); } else { // Handle the download inside of Privacy Browser. // 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(), + new PrepareSaveDialog(this, this, getSupportFragmentManager(), currentWebView.getSettings().getUserAgentString(), 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)); + // Open the file picker with a default file name built from the current domain name. + saveWebpageArchiveActivityResultLauncher.launch(currentWebView.getCurrentDomainName() + ".mht"); + // Consume the event. return true; } else if (menuItemId == R.id.save_image) { // Save image. - // Instantiate the save dialog. - 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. - saveImageFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); + // Open the file picker with a default file name built from the current domain name. + saveWebpageImageActivityResultLauncher.launch(currentWebView.getCurrentDomainName() + ".png"); // Consume the event. return true; @@ -1822,6 +1945,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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()); // Get the current certificate. SslCertificate sslCertificate = currentWebView.getCertificate(); @@ -1849,12 +1973,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook domainsIntent.putExtra("ssl_end_date", endDateLong); } - // Check to see if the current IP addresses have been received. - if (currentWebView.hasCurrentIpAddresses()) { - // Add the current IP addresses to the intent. - domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses()); - } - // Make it so. startActivity(domainsIntent); } else { // Add a new domain. @@ -1878,6 +1996,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook domainsIntent.putExtra("load_domain", newDomainDatabaseId); domainsIntent.putExtra("close_on_back", true); domainsIntent.putExtra("current_url", currentWebView.getUrl()); + domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses()); // Get the current certificate. SslCertificate sslCertificate = currentWebView.getCertificate(); @@ -1905,12 +2024,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook domainsIntent.putExtra("ssl_end_date", endDateLong); } - // Check to see if the current IP addresses have been received. - if (currentWebView.hasCurrentIpAddresses()) { - // Add the current IP addresses to the intent. - domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses()); - } - // Make it so. startActivity(domainsIntent); } @@ -1998,7 +2111,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook Intent requestsIntent = new Intent(this, RequestsActivity.class); // Add the block third-party requests status to the intent. - requestsIntent.putExtra("block_all_third_party_requests", currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)); + requestsIntent.putExtra("block_all_third_party_requests", currentWebView.getBlockAllThirdPartyRequests()); // Make it so. startActivity(requestsIntent); @@ -2056,6 +2169,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add the extra information to the intent. domainsIntent.putExtra("current_url", currentWebView.getUrl()); + domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses()); // Get the current certificate. SslCertificate sslCertificate = currentWebView.getCertificate(); @@ -2083,12 +2197,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook domainsIntent.putExtra("ssl_end_date", endDateLong); } - // Check to see if the current IP addresses have been received. - if (currentWebView.hasCurrentIpAddresses()) { - // Add the current IP addresses to the intent. - domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses()); - } - // Make it so. startActivity(domainsIntent); } else if (menuItemId == R.id.settings) { // Settings. @@ -2246,7 +2354,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook downloadUrlWithExternalApp(linkUrl); } else { // Handle the download inside of Privacy Browser. // 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(), + new PrepareSaveDialog(this, this, getSupportFragmentManager(), currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptCookies()).execute(linkUrl); } @@ -2318,7 +2426,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook downloadUrlWithExternalApp(imageUrl); } else { // Handle the download inside of Privacy Browser. // 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(), + new PrepareSaveDialog(this, this, getSupportFragmentManager(), currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptCookies()).execute(imageUrl); } @@ -2423,7 +2531,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook downloadUrlWithExternalApp(imageUrl); } else { // Handle the download inside of Privacy Browser. // 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(), + new PrepareSaveDialog(this, this, getSupportFragmentManager(), currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptCookies()).execute(imageUrl); } @@ -2450,7 +2558,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook downloadUrlWithExternalApp(linkUrl); } else { // Handle the download inside of Privacy Browser. // 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(), + new PrepareSaveDialog(this, this, getSupportFragmentManager(), currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptCookies()).execute(linkUrl); } @@ -2798,38 +2906,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } break; - - case BROWSE_SAVE_WEBPAGE_REQUEST_CODE: - // Don't do anything if the user pressed back from the file picker. - if (resultCode == Activity.RESULT_OK) { - // Get a handle for the save dialog fragment. - DialogFragment saveWebpageDialogFragment = (DialogFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.save_dialog)); - - // Only update the file name if the dialog still exists. - if (saveWebpageDialogFragment != null) { - // Get a handle for the save webpage dialog. - Dialog saveWebpageDialog = saveWebpageDialogFragment.getDialog(); - - // Remove the incorrect lint warning below that the dialog might be null. - assert saveWebpageDialog != null; - - // Get a handle for the file name edit text. - EditText fileNameEditText = saveWebpageDialog.findViewById(R.id.file_name_edittext); - - // Get the file name URI from the intent. - Uri fileNameUri = returnedIntent.getData(); - - // Get the file name string from the URI. - String fileNameString = fileNameUri.toString(); - - // Set the file name text. - fileNameEditText.setText(fileNameString); - - // Move the cursor to the end of the file name edit text. - fileNameEditText.setSelection(fileNameString.length()); - } - } - break; } } @@ -3050,97 +3126,27 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook startActivity(Intent.createChooser(downloadIntent, getString(R.string.download_with_external_app))); } - 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 file name edit text. - EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext); - - // 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.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]; + public void onSaveUrl(@NonNull String originalUrlString, @NonNull String fileNameString, @NonNull DialogFragment dialogFragment) { + // Store the URL. This will be used in the save URL activity result launcher. + if (originalUrlString.startsWith("data:")) { + // Save the original URL. + saveUrlString = originalUrlString; + } else { + // Get the dialog. + Dialog dialog = dialogFragment.getDialog(); - // Create an integer to track the number of bytes read. - int bytesRead; + // Remove the incorrect lint warning below that the dialog might be null. + assert dialog != null; - // Copy the temporary MHT file input stream to the MHT output stream. - while ((bytesRead = temporaryMhtFileInputStream.read(transferByteArray)) > 0) { - mhtOutputStream.write(transferByteArray, 0, bytesRead); - } + // Get a handle for the dialog URL edit text. + EditText dialogUrlEditText = dialog.findViewById(R.id.url_edittext); - // 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: - // Save the webpage image. - new SaveWebpageImage(this, saveWebpageFilePath, currentWebView).execute(); - break; + // Get the URL from the edit text, which may have been modified. + saveUrlString = dialogUrlEditText.getText().toString(); } + + // Open the file picker. + saveUrlActivityResultLauncher.launch(fileNameString); } // Remove the warning that `OnTouchListener()` needs to override `performClick()`, as the only purpose of setting the `OnTouchListener()` is to make it do nothing. @@ -3209,12 +3215,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Reset the waiting for proxy status. waitingForProxy = false; - // Get a handle for the waiting for proxy dialog. - DialogFragment waitingForProxyDialogFragment = (DialogFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.waiting_for_proxy_dialog)); + // Get a list of the current fragments. + List fragmentList = getSupportFragmentManager().getFragments(); + + // Check each fragment to see if it is a waiting for proxy dialog. Sometimes more than one is displayed. + for (int i = 0; i < fragmentList.size(); i++) { + // Get the fragment tag. + String fragmentTag = fragmentList.get(i).getTag(); - // Dismiss the waiting for proxy dialog if it is displayed. - if (waitingForProxyDialogFragment != null) { - waitingForProxyDialogFragment.dismiss(); + // Check to see if it is the waiting for proxy dialog. + if ((fragmentTag!= null) && fragmentTag.equals(getString(R.string.waiting_for_proxy_dialog))) { + // Dismiss the waiting for proxy dialog. + ((DialogFragment) fragmentList.get(i)).dismiss(); + } } // Reload existing URLs and load any URLs that are waiting for the proxy. @@ -3239,7 +3252,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook loadUrl(nestedScrollWebView, waitingForProxyUrlString); // Reset the waiting for proxy URL string. - nestedScrollWebView.resetWaitingForProxyUrlString(); + nestedScrollWebView.setWaitingForProxyUrlString(""); } else { // No URL is waiting to be loaded. // Reload the existing URL. nestedScrollWebView.reload(); @@ -3314,7 +3327,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onTabReselected(TabLayout.Tab tab) { // Instantiate the View SSL Certificate dialog. - DialogFragment viewSslCertificateDialogFragment = ViewSslCertificateDialog.displayDialog(currentWebView.getWebViewFragmentId()); + DialogFragment viewSslCertificateDialogFragment = ViewSslCertificateDialog.displayDialog(currentWebView.getWebViewFragmentId(), currentWebView.getFavoriteOrDefaultIcon()); // Display the View SSL Certificate dialog. viewSslCertificateDialogFragment.show(getSupportFragmentManager(), getString(R.string.view_ssl_certificate)); @@ -3427,11 +3440,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook defaultProgressViewEndOffset = swipeRefreshLayout.getProgressViewEndOffset(); // Set the refresh color scheme according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - swipeRefreshLayout.setColorSchemeResources(R.color.blue_700); - } else { - swipeRefreshLayout.setColorSchemeResources(R.color.violet_500); - } + swipeRefreshLayout.setColorSchemeResources(R.color.blue_text); // Initialize a color background typed value. TypedValue colorBackgroundTypedValue = new TypedValue(); @@ -3552,9 +3561,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0); } - // Clear the focus from from the URL text box and the WebView. This removes any text selection markers and context menus, which otherwise draw above the open drawers. + // Clear the focus from from the URL text box. This removes any text selection markers and context menus, which otherwise draw above the open drawers. urlEditText.clearFocus(); - currentWebView.clearFocus(); + + // Clear the focus from from the WebView if it is not null, which can happen if a user opens a drawer while the browser is being resumed. + if (currentWebView != null) { + // Clearing the focus from the WebView removes any text selection markers and context menus, which otherwise draw above the open drawers. + currentWebView.clearFocus(); + } } } }); @@ -3767,7 +3781,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Clear any pinned SSL certificate or IP addresses. nestedScrollWebView.clearPinnedSslCertificate(); - nestedScrollWebView.clearPinnedIpAddresses(); + nestedScrollWebView.setPinnedIpAddresses(""); // Reset the favorite icon if specified. if (resetTab) { @@ -3887,19 +3901,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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); - nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASYLIST, - currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1); - nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASYPRIVACY, - currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1); - nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, - currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1); - nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, - currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1); - nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ULTRALIST)) == 1); - nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, - currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)) == 1); - nestedScrollWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, - currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1); + nestedScrollWebView.setEasyListEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1); + nestedScrollWebView.setEasyPrivacyEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1); + nestedScrollWebView.setFanboysAnnoyanceListEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1); + nestedScrollWebView.setFanboysSocialBlockingListEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex( + DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1); + nestedScrollWebView.setUltraListEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ULTRALIST)) == 1); + nestedScrollWebView.setUltraPrivacyEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)) == 1); + nestedScrollWebView.setBlockAllThirdPartyRequests(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1); String userAgentName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT)); int fontSize = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE)); int swipeToRefreshInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH)); @@ -3913,31 +3922,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String pinnedSslIssuedByCName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME)); String pinnedSslIssuedByOName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION)); String pinnedSslIssuedByUName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT)); + Date pinnedSslStartDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE))); + Date pinnedSslEndDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE))); boolean pinnedIpAddresses = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)) == 1); String pinnedHostIpAddresses = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES)); - // Get the pinned SSL date longs. - long pinnedSslStartDateLong = currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)); - long pinnedSslEndDateLong = currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)); - - // Define the pinned SSL date variables. - Date pinnedSslStartDate; - Date pinnedSslEndDate; - - // Set the pinned SSL certificate start date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0. - if (pinnedSslStartDateLong == 0) { - pinnedSslStartDate = null; - } else { - pinnedSslStartDate = new Date(pinnedSslStartDateLong); - } - - // Set the pinned SSL certificate end date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0. - if (pinnedSslEndDateLong == 0) { - pinnedSslEndDate = null; - } else { - pinnedSslEndDate = new Date(pinnedSslEndDateLong); - } - // Close the current host domain settings cursor. currentDomainSettingsCursor.close(); @@ -4135,13 +4124,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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)); - nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASYPRIVACY, sharedPreferences.getBoolean("easyprivacy", true)); - nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, sharedPreferences.getBoolean("fanboys_annoyance_list", true)); - nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, sharedPreferences.getBoolean("fanboys_social_blocking_list", true)); - nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, sharedPreferences.getBoolean("ultralist", true)); - nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, sharedPreferences.getBoolean("ultraprivacy", true)); - nestedScrollWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, sharedPreferences.getBoolean("block_all_third_party_requests", false)); + 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)); // Apply the default cookie setting. cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptCookies()); @@ -4304,8 +4293,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get a handle for the waiting for proxy alert dialog. DialogFragment waitingForProxyDialogFragment = new WaitingForProxyDialog(); - // Display the waiting for proxy alert dialog. - waitingForProxyDialogFragment.show(getSupportFragmentManager(), getString(R.string.waiting_for_proxy_dialog)); + // Try to show the dialog. Sometimes the window is not yet active if returning from Settings. + try { + // Show the waiting for proxy alert dialog. + waitingForProxyDialogFragment.show(getSupportFragmentManager(), getString(R.string.waiting_for_proxy_dialog)); + } catch (Exception waitingForTorException) { + // Add the dialog to the pending dialog array list. It will be displayed in `onStart()`. + pendingDialogsArrayList.add(new PendingDialog(waitingForProxyDialogFragment, getString(R.string.waiting_for_proxy_dialog))); + } } } } catch (PackageManager.NameNotFoundException exception) { // Orbot is not installed. @@ -4314,8 +4309,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get a handle for the Orbot not installed alert dialog. DialogFragment orbotNotInstalledDialogFragment = ProxyNotInstalledDialog.displayDialog(proxyMode); - // Display the Orbot not installed alert dialog. - orbotNotInstalledDialogFragment.show(getSupportFragmentManager(), 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 Orbot not installed alert dialog. + orbotNotInstalledDialogFragment.show(getSupportFragmentManager(), getString(R.string.proxy_not_installed_dialog)); + } catch (Exception orbotNotInstalledException) { + // Add the dialog to the pending dialog array list. It will be displayed in `onStart()`. + pendingDialogsArrayList.add(new PendingDialog(orbotNotInstalledDialogFragment, getString(R.string.proxy_not_installed_dialog))); + } } } break; @@ -4341,8 +4342,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get a handle for the waiting for proxy alert dialog. DialogFragment i2pNotInstalledDialogFragment = ProxyNotInstalledDialog.displayDialog(proxyMode); - // Display the I2P not installed alert dialog. - i2pNotInstalledDialogFragment.show(getSupportFragmentManager(), 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))); + } } } break; @@ -4763,6 +4770,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add the new WebView page. webViewPagerAdapter.addPage(newTabNumber, webViewPager, url, moveToTab); + + // Show the app bar if it is at the bottom of the screen and the new tab is taking focus. + if (bottomAppBar && moveToTab) { + appBarLayout.setVisibility(View.VISIBLE); + } } public void closeTab(View view) { @@ -5157,6 +5169,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } + @SuppressLint("ClickableViewAccessibility") @Override public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url, Boolean restoringState) { // Get a handle for the shared preferences. @@ -5212,9 +5225,6 @@ 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 favorite icon. - nestedScrollWebView.initializeFavoriteIcon(); - // Set the app bar scrolling. nestedScrollWebView.setNestedScrollingEnabled(sharedPreferences.getBoolean("scroll_app_bar", true)); @@ -5378,23 +5388,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the file name from the content disposition. String fileNameString = PrepareSaveDialog.getFileNameFromHeaders(this, contentDisposition, mimetype, downloadUrl); - // Prevent the dialog from displaying if the app window is not visible. - // The download listener continues to function even when the WebView is paused. Attempting to display a dialog in that state leads to a crash. - while (!activity.getWindow().isActive()) { - try { - // The window is not active. Wait 1 second. - wait(1000); - } catch (InterruptedException e) { - // Do nothing. - } - } - // Instantiate the save dialog. - DialogFragment saveDialogFragment = SaveWebpageDialog.saveWebpage(SaveWebpageDialog.SAVE_URL, downloadUrl, formattedFileSizeString, fileNameString, userAgent, + DialogFragment saveDialogFragment = SaveDialog.saveUrl(downloadUrl, formattedFileSizeString, fileNameString, userAgent, 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)); + // Try to show the dialog. The download listener continues to function even when the WebView is paused. Attempting to display a dialog in that state leads to a crash. + try { + // 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)); + } catch (Exception exception) { // The dialog could not be shown. + // Add the dialog to the pending dialog array list. It will be displayed in `onStart()`. + pendingDialogsArrayList.add(new PendingDialog(saveDialogFragment, getString(R.string.save_dialog))); + } } }); @@ -5779,7 +5784,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String currentDomain = currentBaseDomain; // Nobody is happy when comparing null strings. - if ((currentBaseDomain != null) && (url != null)) { + if (url != null) { // Convert the request URL to a URI. Uri requestUri = Uri.parse(url); @@ -5812,7 +5817,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook boolean webViewDisplayed = (webViewPagePosition == tabLayout.getSelectedTabPosition()); // Block third-party requests if enabled. - if (isThirdPartyRequest && nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)) { + if (isThirdPartyRequest && nestedScrollWebView.getBlockAllThirdPartyRequests()) { // Add the result to the resource requests. nestedScrollWebView.addResourceRequest(new String[]{BlocklistHelper.REQUEST_THIRD_PARTY, url}); @@ -5841,7 +5846,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Check UltraList if it is enabled. - if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST)) { + if (nestedScrollWebView.getUltraListEnabled()) { // Check the URL against UltraList. String[] ultraListResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, ultraList); @@ -5881,7 +5886,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Check UltraPrivacy if it is enabled. - if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY)) { + if (nestedScrollWebView.getUltraPrivacyEnabled()) { // Check the URL against UltraPrivacy. String[] ultraPrivacyResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, ultraPrivacy); @@ -5923,7 +5928,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Check EasyList if it is enabled. - if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST)) { + if (nestedScrollWebView.getEasyListEnabled()) { // Check the URL against EasyList. String[] easyListResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, easyList); @@ -5960,7 +5965,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Check EasyPrivacy if it is enabled. - if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY)) { + if (nestedScrollWebView.getEasyPrivacyEnabled()) { // Check the URL against EasyPrivacy. String[] easyPrivacyResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, easyPrivacy); @@ -5998,7 +6003,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Check Fanboy’s Annoyance List if it is enabled. - if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)) { + if (nestedScrollWebView.getFanboysAnnoyanceListEnabled()) { // Check the URL against Fanboy's Annoyance List. String[] fanboysAnnoyanceListResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, fanboysAnnoyanceList); @@ -6035,7 +6040,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook whitelistResultStringArray = new String[] {fanboysAnnoyanceListResults[0], fanboysAnnoyanceListResults[1], fanboysAnnoyanceListResults[2], fanboysAnnoyanceListResults[3], fanboysAnnoyanceListResults[4], fanboysAnnoyanceListResults[5]}; } - } else if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST)) { // Only check Fanboy’s Social Blocking List if Fanboy’s Annoyance List is disabled. + } else if (nestedScrollWebView.getFanboysSocialBlockingListEnabled()) { // Only check Fanboy’s Social Blocking List if Fanboy’s Annoyance List is disabled. // Check the URL against Fanboy's Annoyance List. String[] fanboysSocialListResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, fanboysSocialList); @@ -6135,7 +6140,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Display the formatted URL text. urlEditText.setText(url); - // Apply text highlighting to `urlTextBox`. + // Apply text highlighting to the URL text box. highlightUrlText(); // Hide the keyboard. @@ -6143,7 +6148,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Reset the list of host IP addresses. - nestedScrollWebView.clearCurrentIpAddresses(); + nestedScrollWebView.setCurrentIpAddresses(""); // Get a URI for the current URL. Uri currentUri = Uri.parse(url); @@ -6346,22 +6351,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Store the SSL error handler. nestedScrollWebView.setSslErrorHandler(handler); - // Prevent the dialog from displaying if the app window is not visible. - // The SSL error handler continues to function even when the WebView is paused. Attempting to display a dialog in that state leads to a crash. - while (!activity.getWindow().isActive()) { - try { - // The window is not active. Wait 1 second. - wait(1000); - } catch (InterruptedException e) { - // Do nothing. - } - } - // Instantiate an SSL certificate error alert dialog. DialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error, nestedScrollWebView.getWebViewFragmentId()); - // Show the SSL certificate error dialog. - sslCertificateErrorDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_error)); + // Try to show the dialog. The SSL error handler continues to function even when the WebView is paused. Attempting to display a dialog in that state leads to a crash. + try { + // Show the SSL certificate error dialog. + sslCertificateErrorDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_error)); + } catch (Exception exception) { + // Add the dialog to the pending dialog array list. It will be displayed in `onStart()`. + pendingDialogsArrayList.add(new PendingDialog(sslCertificateErrorDialogFragment, getString(R.string.ssl_certificate_error))); + } } } });