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=5e4d3eeb6a5e1360978659fd027e716985c8edf8;hp=32d50e94c2d21d38548104f80287505c0ee5b0dd;hb=4d51aa9acb8daaec1326f14e5025fde6d1f0dcd8;hpb=81179d84ced6b43360d42a4b44eb8fb329532ff4 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 32d50e94..5e4d3eeb 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -1,5 +1,5 @@ /* - * Copyright © 2015-2019 Soren Stoutner . + * Copyright © 2015-2020 Soren Stoutner . * * Download cookie code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner . * @@ -119,6 +119,7 @@ import com.stoutner.privacybrowser.R; import com.stoutner.privacybrowser.adapters.WebViewPagerAdapter; import com.stoutner.privacybrowser.asynctasks.GetHostIpAddresses; import com.stoutner.privacybrowser.asynctasks.PopulateBlocklists; +import com.stoutner.privacybrowser.asynctasks.SaveUrl; import com.stoutner.privacybrowser.asynctasks.SaveWebpageImage; import com.stoutner.privacybrowser.dialogs.AdConsentDialog; import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog; @@ -131,9 +132,10 @@ import com.stoutner.privacybrowser.dialogs.EditBookmarkDialog; import com.stoutner.privacybrowser.dialogs.EditBookmarkFolderDialog; import com.stoutner.privacybrowser.dialogs.FontSizeDialog; 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.SaveWebpageImageDialog; +import com.stoutner.privacybrowser.dialogs.SaveWebpageDialog; import com.stoutner.privacybrowser.dialogs.SslCertificateErrorDialog; import com.stoutner.privacybrowser.dialogs.StoragePermissionDialog; import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog; @@ -170,10 +172,11 @@ import java.util.Set; // AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21. public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener, - EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveWebpageImageDialog.SaveWebpageImageListener, - StoragePermissionDialog.StoragePermissionDialogListener, UrlHistoryDialog.NavigateHistoryListener, WebViewTabFragment.NewTabListener { + EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, OpenDialog.OpenListener, + PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveWebpageDialog.SaveWebpageListener, StoragePermissionDialog.StoragePermissionDialogListener, + UrlHistoryDialog.NavigateHistoryListener, WebViewTabFragment.NewTabListener { - // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`. + // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxy()`. public static String orbotStatus = "unknown"; // The WebView pager adapter is accessed from `HttpAuthenticationDialog`, `PinnedMismatchDialog`, and `SslCertificateErrorDialog`. It is also used in `onCreate()`, `onResume()`, and `addTab()`. @@ -198,10 +201,25 @@ 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; - // Start activity for result request codes. - private final int FILE_UPLOAD_REQUEST_CODE = 0; - public final static int BROWSE_SAVE_WEBPAGE_IMAGE_REQUEST_CODE = 1; + // Start activity for result request codes. The public static entries are accessed from `OpenDialog()` and `SaveWebpageDialog()`. + public final static int BROWSE_OPEN_REQUEST_CODE = 0; + public final static int BROWSE_SAVE_WEBPAGE_REQUEST_CODE = 1; + private final int BROWSE_FILE_UPLOAD_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()`. + // It will be updated in `applyAppSettings()`, but it needs to be initialized here or the first run of `onPrepareOptionsMenu()` crashes. + public static String proxyMode = ProxyHelper.NONE; + + + // The permission result request codes are used in `onCreateContextMenu()`, `onCloseDownloadLocationPermissionDialog()`, `onRequestPermissionResult()`, `onSaveWebpage()`, + // `onCloseStoragePermissionDialog()`, and `initializeWebView()`. + private final int PERMISSION_DOWNLOAD_FILE_REQUEST_CODE = 0; + private final int PERMISSION_DOWNLOAD_IMAGE_REQUEST_CODE = 1; + private final int PERMISSION_OPEN_REQUEST_CODE = 2; + private final int PERMISSION_SAVE_WEBPAGE_ARCHIVE_REQUEST_CODE = 3; + private final int PERMISSION_SAVE_WEBPAGE_IMAGE_REQUEST_CODE = 4; + private final int PERMISSION_SAVE_WEBPAGE_RAW_REQUEST_CODE = 5; // The current WebView is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`, // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxy()`, and `applyDomainSettings()`. @@ -227,10 +245,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `webViewDefaultUserAgent` is used in `onCreate()` and `onPrepareOptionsMenu()`. private String webViewDefaultUserAgent; - // The proxy mode is used in `onRestart()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `applyAppSettings()`, and `applyProxy()`. - // It will be updated in `applyAppSettings()`, but it needs to be initialized here or the first run of `onPrepareOptionsMenu()` crashes. - private String proxyMode = ProxyHelper.NONE; - // The incognito mode is set in `applyAppSettings()` and used in `initializeWebView()`. private boolean incognitoModeEnabled; @@ -311,14 +325,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `downloadImageUrl` is used in `onCreateContextMenu()` and `onRequestPermissionResult()`. private String downloadImageUrl; - // The save website image file path string is used in `onSaveWebpageImage()` and `onRequestPermissionResult()` - private String saveWebsiteImageFilePath; - - // The permission result request codes are used in `onCreateContextMenu()`, `onCloseDownloadLocationPermissionDialog()`, `onRequestPermissionResult()`, `onSaveWebpageImage()`, - // `onCloseStoragePermissionDialog()`, and `initializeWebView()`. - private final int DOWNLOAD_FILE_REQUEST_CODE = 1; - private final int DOWNLOAD_IMAGE_REQUEST_CODE = 2; - private final int SAVE_WEBPAGE_IMAGE_REQUEST_CODE = 3; + // The file path strings are used in `onSaveWebpageImage()` and `onRequestPermissionResult()` + private String openFilePath; + private String saveWebpageUrl; + private String saveWebpageFilePath; @Override // Remove the warning about needing to override `performClick()` when using an `OnTouchListener` with `WebView`. @@ -874,79 +884,79 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Select the current user agent menu item. A switch statement cannot be used because the user agents are not compile time constants. if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[0])) { // Privacy Browser. // Update the user agent menu item title. - userAgentMenuItem.setTitle(getString(R.string.user_agent) + " - " + getString(R.string.user_agent_privacy_browser)); + userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_privacy_browser)); // Select the Privacy Browser radio box. menu.findItem(R.id.user_agent_privacy_browser).setChecked(true); } else if (currentUserAgent.equals(webViewDefaultUserAgent)) { // WebView Default. // Update the user agent menu item title. - userAgentMenuItem.setTitle(getString(R.string.user_agent) + " - " + getString(R.string.user_agent_webview_default)); + userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_webview_default)); // Select the WebView Default radio box. menu.findItem(R.id.user_agent_webview_default).setChecked(true); } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[2])) { // Firefox on Android. // Update the user agent menu item title. - userAgentMenuItem.setTitle(getString(R.string.user_agent) + " - " + getString(R.string.user_agent_firefox_on_android)); + userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_android)); // Select the Firefox on Android radio box. menu.findItem(R.id.user_agent_firefox_on_android).setChecked(true); } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[3])) { // Chrome on Android. // Update the user agent menu item title. - userAgentMenuItem.setTitle(getString(R.string.user_agent) + " - " + getString(R.string.user_agent_chrome_on_android)); + userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chrome_on_android)); // Select the Chrome on Android radio box. menu.findItem(R.id.user_agent_chrome_on_android).setChecked(true); } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[4])) { // Safari on iOS. // Update the user agent menu item title. - userAgentMenuItem.setTitle(getString(R.string.user_agent) + " - " + getString(R.string.user_agent_safari_on_ios)); + userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_safari_on_ios)); // Select the Safari on iOS radio box. menu.findItem(R.id.user_agent_safari_on_ios).setChecked(true); } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[5])) { // Firefox on Linux. // Update the user agent menu item title. - userAgentMenuItem.setTitle(getString(R.string.user_agent) + " - " + getString(R.string.user_agent_firefox_on_linux)); + userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_linux)); // Select the Firefox on Linux radio box. menu.findItem(R.id.user_agent_firefox_on_linux).setChecked(true); } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[6])) { // Chromium on Linux. // Update the user agent menu item title. - userAgentMenuItem.setTitle(getString(R.string.user_agent) + " - " + getString(R.string.user_agent_chromium_on_linux)); + userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chromium_on_linux)); // Select the Chromium on Linux radio box. menu.findItem(R.id.user_agent_chromium_on_linux).setChecked(true); } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[7])) { // Firefox on Windows. // Update the user agent menu item title. - userAgentMenuItem.setTitle(getString(R.string.user_agent) + " - " + getString(R.string.user_agent_firefox_on_windows)); + userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_windows)); // Select the Firefox on Windows radio box. menu.findItem(R.id.user_agent_firefox_on_windows).setChecked(true); } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[8])) { // Chrome on Windows. // Update the user agent menu item title. - userAgentMenuItem.setTitle(getString(R.string.user_agent) + " - " + getString(R.string.user_agent_chrome_on_windows)); + userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chrome_on_windows)); // Select the Chrome on Windows radio box. menu.findItem(R.id.user_agent_chrome_on_windows).setChecked(true); } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[9])) { // Edge on Windows. // Update the user agent menu item title. - userAgentMenuItem.setTitle(getString(R.string.user_agent) + " - " + getString(R.string.user_agent_edge_on_windows)); + userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_edge_on_windows)); // Select the Edge on Windows radio box. menu.findItem(R.id.user_agent_edge_on_windows).setChecked(true); } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[10])) { // Internet Explorer on Windows. // Update the user agent menu item title. - userAgentMenuItem.setTitle(getString(R.string.user_agent) + " - " + getString(R.string.user_agent_internet_explorer_on_windows)); + userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_internet_explorer_on_windows)); // Select the Internet on Windows radio box. menu.findItem(R.id.user_agent_internet_explorer_on_windows).setChecked(true); } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[11])) { // Safari on macOS. // Update the user agent menu item title. - userAgentMenuItem.setTitle(getString(R.string.user_agent) + " - " + getString(R.string.user_agent_safari_on_macos)); + userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_safari_on_macos)); // Select the Safari on macOS radio box. menu.findItem(R.id.user_agent_safari_on_macos).setChecked(true); } else { // Custom user agent. // Update the user agent menu item title. - userAgentMenuItem.setTitle(getString(R.string.user_agent) + " - " + getString(R.string.user_agent_custom)); + userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_custom)); // Select the Custom radio box. menu.findItem(R.id.user_agent_custom).setChecked(true); @@ -1698,12 +1708,32 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Consume the event. return true; + case R.id.save_url: + // Instantiate the save dialog. + DialogFragment saveDialogFragment = SaveWebpageDialog.saveUrl(StoragePermissionDialog.SAVE, currentWebView.getCurrentUrl()); + + // Show the save dialog. It must be named `save_dialog` so that the file picked can update the file name. + saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); + + // Consume the event. + return true; + + case R.id.save_as_archive: + // Instantiate the save webpage archive dialog. + DialogFragment saveWebpageArchiveDialogFragment = SaveWebpageDialog.saveUrl(StoragePermissionDialog.SAVE_AS_ARCHIVE, currentWebView.getCurrentUrl()); + + // Show the save webpage archive dialog. It must be named `save_dialog` so that the file picked can update the file name. + saveWebpageArchiveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); + + // Consume the event. + return true; + case R.id.save_as_image: - // Instantiate the save webpage image dialog. - DialogFragment saveWebpageImageDialogFragment = new SaveWebpageImageDialog(); + // Instantiate the save webpage image dialog. It must be named `save_webpage` so that the file picked can update the file name. + DialogFragment saveWebpageImageDialogFragment = SaveWebpageDialog.saveUrl(StoragePermissionDialog.SAVE_AS_IMAGE, currentWebView.getCurrentUrl()); - // Show the save webpage image dialog. - saveWebpageImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_as_image)); + // Show the save webpage image dialog. It must be named `save_dialog` so that the file picked can update the file name. + saveWebpageImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); // Consume the event. return true; @@ -1851,6 +1881,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history)); break; + case R.id.open: + // Instantiate the open file dialog. + DialogFragment openDialogFragment = new OpenDialog(); + + // Show the open file dialog. + openDialogFragment.show(getSupportFragmentManager(), getString(R.string.open)); + break; + case R.id.requests: // Populate the resource requests. RequestsActivity.resourceRequests = currentWebView.getResourceRequests(); @@ -2023,8 +2061,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get handles for the system managers. final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - FragmentManager fragmentManager = getSupportFragmentManager(); - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); // Remove the lint errors below that the clipboard manager might be null. assert clipboardManager != null; @@ -2085,38 +2121,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; }); - // Add a Download URL entry. - menu.add(R.string.download_url).setOnMenuItemClickListener((MenuItem item) -> { - // Check if the download should be processed by an external app. - if (sharedPreferences.getBoolean("download_with_external_app", false)) { // Download with an external app. - openUrlWithExternalApp(linkUrl); - } else { // Download with Android's download manager. - // Check to see if the storage permission has already been granted. - if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { // The storage permission needs to be requested. - // Store the variables for future use by `onRequestPermissionsResult()`. - downloadUrl = linkUrl; - downloadContentDisposition = "none"; - downloadContentLength = -1; - - // Show a dialog if the user has previously denied the permission. - if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. - // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_FILE. - DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_FILE); - - // Show the download location permission alert dialog. The permission will be requested when the the dialog is closed. - downloadLocationPermissionDialogFragment.show(fragmentManager, getString(R.string.download_location)); - } else { // Show the permission request directly. - // Request the permission. The download dialog will be launched by `onRequestPermissionResult()`. - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE); - } - } else { // The storage permission has already been granted. - // Get a handle for the download file alert dialog. - DialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(linkUrl, "none", -1); + // Add a Save URL entry. + menu.add(R.string.save_url).setOnMenuItemClickListener((MenuItem item) -> { + // Instantiate the save dialog. + DialogFragment saveDialogFragment = SaveWebpageDialog.saveUrl(StoragePermissionDialog.SAVE, linkUrl); - // Show the download file alert dialog. - downloadFileDialogFragment.show(fragmentManager, getString(R.string.download)); - } - } + // Show the save dialog. It must be named `save_dialog` so that the file picked can update the file name. + saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); // Consume the event. return true; @@ -2193,36 +2204,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; }); - // Add a Download Image entry. - menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> { - // Check if the download should be processed by an external app. - if (sharedPreferences.getBoolean("download_with_external_app", false)) { // Download with an external app. - openUrlWithExternalApp(imageUrl); - } else { // Download with Android's download manager. - // Check to see if the storage permission has already been granted. - if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { // The storage permission needs to be requested. - // Store the image URL for use by `onRequestPermissionResult()`. - downloadImageUrl = imageUrl; - - // Show a dialog if the user has previously denied the permission. - if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. - // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE. - DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_IMAGE); - - // Show the download location permission alert dialog. The permission will be requested when the dialog is closed. - downloadLocationPermissionDialogFragment.show(fragmentManager, getString(R.string.download_location)); - } else { // Show the permission request directly. - // Request the permission. The download dialog will be launched by `onRequestPermissionResult()`. - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_IMAGE_REQUEST_CODE); - } - } else { // The storage permission has already been granted. - // Get a handle for the download image alert dialog. - DialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl); + // Add a Save Image entry. + menu.add(R.string.save_image).setOnMenuItemClickListener((MenuItem item) -> { + // Instantiate the save dialog. + DialogFragment saveDialogFragment = SaveWebpageDialog.saveUrl(StoragePermissionDialog.SAVE, imageUrl); - // Show the download image alert dialog. - downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)); - } - } + // Show the save dialog. It must be named `save_dialog` so that the file picked can update the file name. + saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); // Consume the event. return true; @@ -2318,41 +2306,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; }); - // Add a Download Image entry. - menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> { - // Check if the download should be processed by an external app. - if (sharedPreferences.getBoolean("download_with_external_app", false)) { // Download with an external app. - openUrlWithExternalApp(imageUrl); - } else { // Download with Android's download manager. - // Check to see if the storage permission has already been granted. - if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { // The storage permission needs to be requested. - // Store the image URL for use by `onRequestPermissionResult()`. - downloadImageUrl = imageUrl; - - // Show a dialog if the user has previously denied the permission. - if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. - // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE. - DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_IMAGE); - - // Show the download location permission alert dialog. The permission will be requested when the dialog is closed. - downloadLocationPermissionDialogFragment.show(fragmentManager, getString(R.string.download_location)); - } else { // Show the permission request directly. - // Request the permission. The download dialog will be launched by `onRequestPermissionResult()`. - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_IMAGE_REQUEST_CODE); - } - } else { // The storage permission has already been granted. - // Get a handle for the download image alert dialog. - DialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl); - - // Show the download image alert dialog. - downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)); - } - } - - // Consume the event. - return true; - }); - // Add a Copy URL entry. menu.add(R.string.copy_url).setOnMenuItemClickListener((MenuItem item) -> { // Save the link URL in a clip data. @@ -2365,6 +2318,28 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; }); + menu.add(R.string.save_image).setOnMenuItemClickListener((MenuItem item) -> { + // Instantiate the save dialog. + DialogFragment saveDialogFragment = SaveWebpageDialog.saveUrl(StoragePermissionDialog.SAVE, imageUrl); + + // Show the save raw dialog. It must be named `save_dialog` so that the file picked can update the file name. + saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); + + // Consume the event. + return true; + }); + + menu.add(R.string.save_url).setOnMenuItemClickListener((MenuItem item) -> { + // Instantiate the save dialog. + DialogFragment saveDialogFragment = SaveWebpageDialog.saveUrl(StoragePermissionDialog.SAVE, linkUrl); + + // Show the save raw dialog. It must be named `save_dialog` so that the file picked can update the file name. + saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); + + // Consume the event. + return true; + }); + // Add an Open with App entry. menu.add(R.string.open_with_app).setOnMenuItemClickListener((MenuItem item) -> { // Open the link URL with an external app. @@ -2632,66 +2607,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook switch (downloadType) { case DownloadLocationPermissionDialog.DOWNLOAD_FILE: // Request the WRITE_EXTERNAL_STORAGE permission with a file request code. - ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE); + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_DOWNLOAD_FILE_REQUEST_CODE); break; case DownloadLocationPermissionDialog.DOWNLOAD_IMAGE: // Request the WRITE_EXTERNAL_STORAGE permission with an image request code. - ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_IMAGE_REQUEST_CODE); - break; - } - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - // Get a handle for the fragment manager. - FragmentManager fragmentManager = getSupportFragmentManager(); - - switch (requestCode) { - case DOWNLOAD_FILE_REQUEST_CODE: - // Show the download file alert dialog. When the dialog closes, the correct command will be used based on the permission status. - DialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(downloadUrl, downloadContentDisposition, downloadContentLength); - - // On API 23, displaying the fragment must be delayed or the app will crash. - if (Build.VERSION.SDK_INT == 23) { - new Handler().postDelayed(() -> downloadFileDialogFragment.show(fragmentManager, getString(R.string.download)), 500); - } else { - downloadFileDialogFragment.show(fragmentManager, getString(R.string.download)); - } - - // Reset the download variables. - downloadUrl = ""; - downloadContentDisposition = ""; - downloadContentLength = 0; - break; - - case DOWNLOAD_IMAGE_REQUEST_CODE: - // Show the download image alert dialog. When the dialog closes, the correct command will be used based on the permission status. - DialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(downloadImageUrl); - - // On API 23, displaying the fragment must be delayed or the app will crash. - if (Build.VERSION.SDK_INT == 23) { - new Handler().postDelayed(() -> downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)), 500); - } else { - downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)); - } - - // Reset the image URL variable. - downloadImageUrl = ""; - break; - - case SAVE_WEBPAGE_IMAGE_REQUEST_CODE: - // Check to see if the storage permission was granted. If the dialog was canceled the grant result will be empty. - if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // The storage permission was granted. - // Save the webpage image. - new SaveWebpageImage(this, currentWebView).execute(saveWebsiteImageFilePath); - } else { // The storage permission was not granted. - // Display an error snackbar. - Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show(); - } - - // Reset the save website image file path. - saveWebsiteImageFilePath = ""; + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_DOWNLOAD_IMAGE_REQUEST_CODE); break; } } @@ -2940,47 +2861,89 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Process the results of a file browse. @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { + public void onActivityResult(int requestCode, int resultCode, Intent returnedIntent) { // Run the default commands. - super.onActivityResult(requestCode, resultCode, data); + super.onActivityResult(requestCode, resultCode, returnedIntent); // Run the commands that correlate to the specified request code. switch (requestCode) { - case FILE_UPLOAD_REQUEST_CODE: + case BROWSE_FILE_UPLOAD_REQUEST_CODE: // File uploads only work on API >= 21. if (Build.VERSION.SDK_INT >= 21) { // Pass the file to the WebView. - fileChooserCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data)); + fileChooserCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, returnedIntent)); } break; - case BROWSE_SAVE_WEBPAGE_IMAGE_REQUEST_CODE: + 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 saveWebpageImageDialogFragment= (DialogFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.save_as_image)); + DialogFragment saveWebpageDialogFragment = (DialogFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.save_dialog)); // Only update the file name if the dialog still exists. - if (saveWebpageImageDialogFragment != null) { - // Get a handle for the save webpage image dialog. - Dialog saveWebpageImageDialog = saveWebpageImageDialogFragment.getDialog(); + 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 saveWebpageImageDialog != null; + assert saveWebpageDialog != null; // Get a handle for the file name edit text. - EditText fileNameEditText = saveWebpageImageDialog.findViewById(R.id.file_name_edittext); + EditText fileNameEditText = saveWebpageDialog.findViewById(R.id.file_name_edittext); + TextView fileExistsWarningTextView = saveWebpageDialog.findViewById(R.id.file_exists_warning_textview); // Instantiate the file name helper. FileNameHelper fileNameHelper = new FileNameHelper(); // Get the file path if it isn't null. - if (data.getData() != null) { + if (returnedIntent.getData() != null) { // Convert the file name URI to a file name path. - String fileNamePath = fileNameHelper.convertUriToFileNamePath(data.getData()); + String fileNamePath = fileNameHelper.convertUriToFileNamePath(returnedIntent.getData()); // Set the file name path as the text of the file name edit text. fileNameEditText.setText(fileNamePath); + + // Move the cursor to the end of the file name edit text. + fileNameEditText.setSelection(fileNamePath.length()); + + // Hide the file exists warning. + fileExistsWarningTextView.setVisibility(View.GONE); + } + } + } + break; + + case BROWSE_OPEN_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 open dialog fragment. + DialogFragment openDialogFragment = (DialogFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.open)); + + // Only update the file name if the dialog still exists. + if (openDialogFragment != null) { + // Get a handle for the open dialog. + Dialog openDialog = openDialogFragment.getDialog(); + + // Remove the incorrect lint warning below that the dialog might be null. + assert openDialog != null; + + // Get a handle for the file name edit text. + EditText fileNameEditText = openDialog.findViewById(R.id.file_name_edittext); + + // Instantiate the file name helper. + FileNameHelper fileNameHelper = new FileNameHelper(); + + // Get the file path if it isn't null. + if (returnedIntent.getData() != null) { + // Convert the file name URI to a file name path. + String fileNamePath = fileNameHelper.convertUriToFileNamePath(returnedIntent.getData()); + + // Set the file name path as the text of the file name edit text. + fileNameEditText.setText(fileNamePath); + + // Move the cursor to the end of the file name edit text. + fileNameEditText.setSelection(fileNamePath.length()); } } } @@ -3135,7 +3098,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onSaveWebpageImage(DialogFragment dialogFragment) { + public void onOpen(DialogFragment dialogFragment) { // Get the dialog. Dialog dialog = dialogFragment.getDialog(); @@ -3146,14 +3109,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext); // Get the file path string. - saveWebsiteImageFilePath = fileNameEditText.getText().toString(); + openFilePath = fileNameEditText.getText().toString(); // Check to see if the storage permission is needed. if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted. - // Save the webpage image. - new SaveWebpageImage(this, currentWebView).execute(saveWebsiteImageFilePath); + // Open the file. + currentWebView.loadUrl("file://" + openFilePath); } else { // The storage permission has not been granted. - // Get the external private directory `File`. + // Get the external private directory file. File externalPrivateDirectoryFile = getExternalFilesDir(null); // Remove the incorrect lint error below that the file might be null. @@ -3163,29 +3126,238 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String externalPrivateDirectory = externalPrivateDirectoryFile.toString(); // Check to see if the file path is in the external private directory. - if (saveWebsiteImageFilePath.startsWith(externalPrivateDirectory)) { // The file path is in the external private directory. - // Save the webpage image. - new SaveWebpageImage(this, currentWebView).execute(saveWebsiteImageFilePath); + if (openFilePath.startsWith(externalPrivateDirectory)) { // the file path is in the external private directory. + // Open the file. + currentWebView.loadUrl("file://" + openFilePath); } else { // The file path is in a public directory. // Check if the user has previously denied the storage permission. if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. // Instantiate the storage permission alert dialog. - DialogFragment storagePermissionDialogFragment = new StoragePermissionDialog(); + DialogFragment storagePermissionDialogFragment = StoragePermissionDialog.displayDialog(StoragePermissionDialog.OPEN); + + // Show the storage permission alert dialog. The permission will be requested the the dialog is closed. + storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission)); + } else { // Show the permission request directly. + // Request the write external storage permission. The file will be opened when it finishes. + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_OPEN_REQUEST_CODE); + } + } + } + } + + @Override + public void onSaveWebpage(int saveType, 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 urlEditText = dialog.findViewById(R.id.url_edittext); + EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext); + + // Get the strings from the edit texts. + saveWebpageUrl = urlEditText.getText().toString(); + saveWebpageFilePath = fileNameEditText.getText().toString(); + + // Check to see if the storage permission is needed. + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted. + //Save the webpage according to the save type. + switch (saveType) { + case StoragePermissionDialog.SAVE: + // Save the URL. + new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl); + break; + + case StoragePermissionDialog.SAVE_AS_ARCHIVE: + // Save the webpage archive. + currentWebView.saveWebArchive(saveWebpageFilePath); + break; + + case StoragePermissionDialog.SAVE_AS_IMAGE: + // Save the webpage image. + new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath); + break; + } + } else { // The storage permission has not been granted. + // Get the external private directory file. + File externalPrivateDirectoryFile = getExternalFilesDir(null); + + // Remove the incorrect lint error below that the file might be null. + assert externalPrivateDirectoryFile != null; + + // Get the external private directory string. + String externalPrivateDirectory = externalPrivateDirectoryFile.toString(); + + // Check to see if the file path is in the external private directory. + if (saveWebpageFilePath.startsWith(externalPrivateDirectory)) { // The file path is in the external private directory. + // Save the webpage according to the save type. + switch (saveType) { + case StoragePermissionDialog.SAVE: + // Save the URL. + new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl); + break; + + case StoragePermissionDialog.SAVE_AS_ARCHIVE: + // Save the webpage archive. + currentWebView.saveWebArchive(saveWebpageFilePath); + break; + + case StoragePermissionDialog.SAVE_AS_IMAGE: + // Save the webpage image. + new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath); + break; + } + } else { // The file path is in a public directory. + // Check if the user has previously denied the storage permission. + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. + // Instantiate the storage permission alert dialog. + DialogFragment storagePermissionDialogFragment = StoragePermissionDialog.displayDialog(saveType); // Show the storage permission alert dialog. The permission will be requested when the dialog is closed. storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission)); } else { // Show the permission request directly. - // Request the write external storage permission. The webpage image will be saved when it finishes. - ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, SAVE_WEBPAGE_IMAGE_REQUEST_CODE); + switch (saveType) { + case StoragePermissionDialog.SAVE: + // Request the write external storage permission. The URL will be saved when it finishes. + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_WEBPAGE_RAW_REQUEST_CODE); + + case StoragePermissionDialog.SAVE_AS_ARCHIVE: + // Request the write external storage permission. The webpage archive will be saved when it finishes. + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_WEBPAGE_ARCHIVE_REQUEST_CODE); + break; + + case StoragePermissionDialog.SAVE_AS_IMAGE: + // Request the write external storage permission. The webpage image will be saved when it finishes. + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_WEBPAGE_IMAGE_REQUEST_CODE); + break; + } } } } } @Override - public void onCloseStoragePermissionDialog() { - // Request the write external storage permission. The webpage image will be saved when it finishes. - ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, SAVE_WEBPAGE_IMAGE_REQUEST_CODE); + public void onCloseStoragePermissionDialog(int requestType) { + switch (requestType) { + case StoragePermissionDialog.OPEN: + // Request the write external storage permission. The file will be opened when it finishes. + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_OPEN_REQUEST_CODE); + break; + + case StoragePermissionDialog.SAVE: + // Request the write external storage permission. The URL will be saved when it finishes. + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_WEBPAGE_RAW_REQUEST_CODE); + break; + + case StoragePermissionDialog.SAVE_AS_ARCHIVE: + // Request the write external storage permission. The webpage archive will be saved when it finishes. + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_WEBPAGE_ARCHIVE_REQUEST_CODE); + break; + + case StoragePermissionDialog.SAVE_AS_IMAGE: + // Request the write external storage permission. The webpage image will be saved when it finishes. + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_WEBPAGE_IMAGE_REQUEST_CODE); + break; + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + // Get a handle for the fragment manager. + FragmentManager fragmentManager = getSupportFragmentManager(); + + switch (requestCode) { + case PERMISSION_DOWNLOAD_FILE_REQUEST_CODE: + // Show the download file alert dialog. When the dialog closes, the correct command will be used based on the permission status. + DialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(downloadUrl, downloadContentDisposition, downloadContentLength); + + // On API 23, displaying the fragment must be delayed or the app will crash. + if (Build.VERSION.SDK_INT == 23) { + new Handler().postDelayed(() -> downloadFileDialogFragment.show(fragmentManager, getString(R.string.download)), 500); + } else { + downloadFileDialogFragment.show(fragmentManager, getString(R.string.download)); + } + + // Reset the download variables. + downloadUrl = ""; + downloadContentDisposition = ""; + downloadContentLength = 0; + break; + + case PERMISSION_DOWNLOAD_IMAGE_REQUEST_CODE: + // Show the download image alert dialog. When the dialog closes, the correct command will be used based on the permission status. + DialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(downloadImageUrl); + + // On API 23, displaying the fragment must be delayed or the app will crash. + if (Build.VERSION.SDK_INT == 23) { + new Handler().postDelayed(() -> downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)), 500); + } else { + downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)); + } + + // Reset the image URL variable. + downloadImageUrl = ""; + break; + + case PERMISSION_OPEN_REQUEST_CODE: + // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty. + if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // The storage permission was granted. + // Load the file. + currentWebView.loadUrl("file://" + openFilePath); + } else { // The storage permission was not granted. + // Display an error snackbar. + Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show(); + } + + // Reset the open file path. + openFilePath = ""; + break; + + case PERMISSION_SAVE_WEBPAGE_ARCHIVE_REQUEST_CODE: + // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty. + if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // The storage permission was granted. + // Save the webpage archive. + currentWebView.saveWebArchive(saveWebpageFilePath); + } else { // The storage permission was not granted. + // Display an error snackbar. + Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show(); + } + + // Reset the save webpage file path. + saveWebpageFilePath = ""; + break; + + case PERMISSION_SAVE_WEBPAGE_IMAGE_REQUEST_CODE: + // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty. + if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // The storage permission was granted. + // Save the webpage image. + new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath); + } else { // The storage permission was not granted. + // Display an error snackbar. + Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show(); + } + + // Reset the save webpage file path. + saveWebpageFilePath = ""; + break; + + case PERMISSION_SAVE_WEBPAGE_RAW_REQUEST_CODE: + // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty. + if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // The storage permission was granted. + // Save the raw URL. + new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl); + } else { // The storage permission was not granted. + // Display an error snackbar. + Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show(); + } + + // Reset the save strings. + saveWebpageUrl = ""; + saveWebpageFilePath = ""; + break; + } } private void applyAppSettings() { @@ -3417,7 +3589,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook waitingForProxyDialogFragment.dismiss(); } - // Load any URLs that are waiting for the proxy. + // Reload existing URLs and load any URLs that are waiting for the proxy. for (int i = 0; i < webViewPagerAdapter.getCount(); i++) { // Get the WebView tab fragment. WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i); @@ -3434,12 +3606,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String waitingForProxyUrlString = nestedScrollWebView.getWaitingForProxyUrlString(); // Load the pending URL if it exists. - if (!waitingForProxyUrlString.isEmpty()) { + if (!waitingForProxyUrlString.isEmpty()) { // A URL is waiting to be loaded. // Load the URL. loadUrl(nestedScrollWebView, waitingForProxyUrlString); // Reset the waiting for proxy URL string. nestedScrollWebView.resetWaitingForProxyUrlString(); + } else { // No URL is waiting to be loaded. + // Reload the existing URL. + nestedScrollWebView.reload(); } } } @@ -3465,12 +3640,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Listen for touches on the navigation menu. navigationView.setNavigationItemSelectedListener(this); - // Get handles for the navigation menu and the back and forward menu items. The menu is zero-based. + // Get handles for the navigation menu and the back and forward menu items. The menu is 0 based. Menu navigationMenu = navigationView.getMenu(); MenuItem navigationBackMenuItem = navigationMenu.getItem(2); MenuItem navigationForwardMenuItem = navigationMenu.getItem(3); MenuItem navigationHistoryMenuItem = navigationMenu.getItem(4); - MenuItem navigationRequestsMenuItem = navigationMenu.getItem(5); + MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6); // Update the web view pager every time a tab is modified. webViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @@ -3732,7 +3907,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook drawerHeaderPaddingTop = statusBarPixelSize + (int) (4 * screenDensity); drawerHeaderPaddingBottom = (int) (8 * screenDensity); - // The drawer listener is used to update the navigation menu.` + // The drawer listener is used to update the navigation menu. drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() { @Override public void onDrawerSlide(@NonNull View drawerView, float slideOffset) { @@ -4316,12 +4491,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook AppBarLayout appBarLayout = findViewById(R.id.appbar_layout); // Set the proxy according to the mode. `this` refers to the current activity where an alert dialog might be displayed. - ProxyHelper.setProxy(getApplicationContext(), proxyMode); + ProxyHelper.setProxy(getApplicationContext(), appBarLayout, proxyMode); // Reset the waiting for proxy tracker. waitingForProxy = false; - // Update the user interface and reload the WebViews is requested. + // Update the user interface and reload the WebViews if requested. switch (proxyMode) { case ProxyHelper.NONE: // Set the default app bar layout background. @@ -4499,20 +4674,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - private void openUrlWithExternalApp(String url) { - // Create a download intent. Not specifying the action type will display the maximum number of options. - Intent downloadIntent = new Intent(); - - // Set the URI and the MIME type. Specifying `text/html` displays a good number of options. - downloadIntent.setDataAndType(Uri.parse(url), "text/html"); - - // Flag the intent to open in a new task. - downloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - - // Show the chooser. - startActivity(Intent.createChooser(downloadIntent, getString(R.string.open_with))); - } - private void highlightUrlText() { // Get a handle for the URL edit text. EditText urlEditText = findViewById(R.id.url_edittext); @@ -4523,12 +4684,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String urlString = urlEditText.getText().toString(); // Highlight the URL according to the protocol. - if (urlString.startsWith("file://")) { // This is a file URL. - // De-emphasize only the protocol. - urlEditText.getText().setSpan(initialGrayColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } else if (urlString.startsWith("content://")) { - // De-emphasize only the protocol. - urlEditText.getText().setSpan(initialGrayColorSpan, 0, 10, Spanned.SPAN_INCLUSIVE_INCLUSIVE); + if (urlString.startsWith("file://") || urlString.startsWith("content://")) { // This is a file or content URL. + // De-emphasize everything before the file name. + urlEditText.getText().setSpan(initialGrayColorSpan, 0, urlString.lastIndexOf("/") + 1,Spanned.SPAN_INCLUSIVE_INCLUSIVE); } else { // This is a web URL. // Get the index of the `/` immediately after the domain name. int endOfDomainName = urlString.indexOf("/", (urlString.indexOf("//") + 2)); @@ -5266,7 +5424,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook downloadLocationPermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.download_location)); } else { // Show the permission request directly. // Request the permission. The download dialog will be launched by `onRequestPermissionResult()`. - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE); + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_DOWNLOAD_FILE_REQUEST_CODE); } } else { // The storage permission has already been granted. // Get a handle for the download file alert dialog. @@ -5563,7 +5721,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook Intent fileChooserIntent = fileChooserParams.createIntent(); // Open the file chooser. - startActivityForResult(fileChooserIntent, FILE_UPLOAD_REQUEST_CODE); + startActivityForResult(fileChooserIntent, BROWSE_FILE_UPLOAD_REQUEST_CODE); } return true; } @@ -5679,7 +5837,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook Menu navigationMenu = navigationView.getMenu(); // Get a handle for the navigation requests menu item. The menu is 0 based. - MenuItem navigationRequestsMenuItem = navigationMenu.getItem(5); + MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6); // Create an empty web resource response to be used if the resource request is blocked. WebResourceResponse emptyWebResourceResponse = new WebResourceResponse("text/plain", "utf8", new ByteArrayInputStream("".getBytes()));