X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Fasynctasks%2FPrepareSaveDialog.java;h=93bd110e7d5ad5edb4d8a1a23f9adf9f0b58cef1;hp=9b1dcd0974a27b8d011abc3b5ae7423de0327c75;hb=2bf00b70e1626bb941ec517fc6749f18ddb255c6;hpb=f52255e6eeb1a6be9f190e733563cc37b5d1f2b5 diff --git a/app/src/main/java/com/stoutner/privacybrowser/asynctasks/PrepareSaveDialog.java b/app/src/main/java/com/stoutner/privacybrowser/asynctasks/PrepareSaveDialog.java index 9b1dcd09..93bd110e 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/asynctasks/PrepareSaveDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/asynctasks/PrepareSaveDialog.java @@ -1,5 +1,5 @@ /* - * Copyright © 2020 Soren Stoutner . + * Copyright © 2020-2021 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -24,12 +24,15 @@ import android.content.Context; import android.net.Uri; import android.os.AsyncTask; import android.webkit.CookieManager; +import android.webkit.MimeTypeMap; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.FragmentManager; import com.stoutner.privacybrowser.R; -import com.stoutner.privacybrowser.dialogs.SaveDialog; +import com.stoutner.privacybrowser.activities.MainWebViewActivity; +import com.stoutner.privacybrowser.definitions.PendingDialog; +import com.stoutner.privacybrowser.dialogs.SaveWebpageDialog; import com.stoutner.privacybrowser.helpers.ProxyHelper; import java.lang.ref.WeakReference; @@ -40,14 +43,14 @@ import java.text.NumberFormat; public class PrepareSaveDialog extends AsyncTask { // Define weak references. - private WeakReference activityWeakReference; - private WeakReference contextWeakReference; - private WeakReference fragmentManagerWeakReference; + private final WeakReference activityWeakReference; + private final WeakReference contextWeakReference; + private final WeakReference fragmentManagerWeakReference; // Define the class variables. - private int saveType; - private String userAgent; - private boolean cookiesEnabled; + private final int saveType; + private final String userAgent; + private final boolean cookiesEnabled; private String urlString; // The public constructor. @@ -78,80 +81,106 @@ public class PrepareSaveDialog extends AsyncTask { // Get the URL string. urlString = urlToSave[0]; - // Define the file name string. + // Define the strings. + String formattedFileSize; String fileNameString; - // Initialize the formatted file size string. - String formattedFileSize = context.getString(R.string.unknown_size); + // Populate the file size and name strings. + if (urlString.startsWith("data:")) { // The URL contains the entire data of an image. + // Remove `data:` from the beginning of the URL. + String urlWithoutData = urlString.substring(5); - // Because everything relating to requesting data from a webserver can throw errors, the entire section must catch exceptions. - try { - // Convert the URL string to a URL. - URL url = new URL(urlString); + // Get the URL MIME type, which end with a `;`. + String urlMimeType = urlWithoutData.substring(0, urlWithoutData.indexOf(";")); - // Instantiate the proxy helper. - ProxyHelper proxyHelper = new ProxyHelper(); + // Get the Base64 data, which begins after a `,`. + String base64DataString = urlWithoutData.substring(urlWithoutData.indexOf(",") + 1); - // Get the current proxy. - Proxy proxy = proxyHelper.getCurrentProxy(context); + // Calculate the file size of the data URL. Each Base64 character represents 6 bits. + formattedFileSize = NumberFormat.getInstance().format(base64DataString.length() * 3 / 4) + " " + context.getString(R.string.bytes); - // Open a connection to the URL. No data is actually sent at this point. - HttpURLConnection httpUrlConnection = (HttpURLConnection) url.openConnection(proxy); + // Set the file name according to the MIME type. + fileNameString = context.getString(R.string.file) + "." + MimeTypeMap.getSingleton().getExtensionFromMimeType(urlMimeType); + } else { // The URL refers to the location of the data. + // Initialize the formatted file size string. + formattedFileSize = context.getString(R.string.unknown_size); - // Add the user agent to the header property. - httpUrlConnection.setRequestProperty("User-Agent", userAgent); + // Because everything relating to requesting data from a webserver can throw errors, the entire section must catch exceptions. + try { + // Convert the URL string to a URL. + URL url = new URL(urlString); - // Add the cookies if they are enabled. - if (cookiesEnabled) { - // Get the cookies for the current domain. - String cookiesString = CookieManager.getInstance().getCookie(url.toString()); + // Instantiate the proxy helper. + ProxyHelper proxyHelper = new ProxyHelper(); - // only add the cookies if they are not null. - if (cookiesString != null) { - // Add the cookies to the header property. - httpUrlConnection.setRequestProperty("Cookie", cookiesString); - } - } + // Get the current proxy. + Proxy proxy = proxyHelper.getCurrentProxy(context); - // The actual network request is in a `try` bracket so that `disconnect()` is run in the `finally` section even if an error is encountered in the main block. - try { - // Get the status code. This initiates a network connection. - int responseCode = httpUrlConnection.getResponseCode(); - - // Check the response code. - if (responseCode >= 400) { // The response code is an error message. - // Set the formatted file size to indicate a bad URL. - formattedFileSize = context.getString(R.string.invalid_url); - - // Set the file name according to the URL. - fileNameString = getFileNameFromUrl(context, urlString); - } else { // The response code is not an error message. - // Get the content length and disposition headers. - String contentLengthString = httpUrlConnection.getHeaderField("Content-Length"); - String contentDispositionString = httpUrlConnection.getHeaderField("Content-Disposition"); - - // Only process the content length string if it isn't null. - if (contentLengthString != null) { - // Convert the content length string to a long. - long fileSize = Long.parseLong(contentLengthString); - - // Format the file size. - formattedFileSize = NumberFormat.getInstance().format(fileSize) + " " + context.getString(R.string.bytes); + // Open a connection to the URL. No data is actually sent at this point. + HttpURLConnection httpUrlConnection = (HttpURLConnection) url.openConnection(proxy); + + // Add the user agent to the header property. + httpUrlConnection.setRequestProperty("User-Agent", userAgent); + + // Add the cookies if they are enabled. + if (cookiesEnabled) { + // Get the cookies for the current domain. + String cookiesString = CookieManager.getInstance().getCookie(url.toString()); + + // only add the cookies if they are not null. + if (cookiesString != null) { + // Add the cookies to the header property. + httpUrlConnection.setRequestProperty("Cookie", cookiesString); } + } - // Get the file name string from the content disposition. - fileNameString = getFileNameFromContentDisposition(context, contentDispositionString, urlString); + // The actual network request is in a `try` bracket so that `disconnect()` is run in the `finally` section even if an error is encountered in the main block. + try { + // Get the status code. This initiates a network connection. + int responseCode = httpUrlConnection.getResponseCode(); + + // Check the response code. + if (responseCode >= 400) { // The response code is an error message. + // Set the formatted file size to indicate a bad URL. + formattedFileSize = context.getString(R.string.invalid_url); + + // Set the file name according to the URL. + fileNameString = getFileNameFromUrl(context, urlString, null); + } else { // The response code is not an error message. + // Get the headers. + String contentLengthString = httpUrlConnection.getHeaderField("Content-Length"); + String contentDispositionString = httpUrlConnection.getHeaderField("Content-Disposition"); + String contentTypeString = httpUrlConnection.getContentType(); + + // Remove anything after the MIME type in the content type string. + if (contentTypeString.contains(";")) { + // Remove everything beginning with the `;`. + contentTypeString = contentTypeString.substring(0, contentTypeString.indexOf(";")); + } + + // Only process the content length string if it isn't null. + if (contentLengthString != null) { + // Convert the content length string to a long. + long fileSize = Long.parseLong(contentLengthString); + + // Format the file size. + formattedFileSize = NumberFormat.getInstance().format(fileSize) + " " + context.getString(R.string.bytes); + } + + // Get the file name string from the content disposition. + fileNameString = getFileNameFromHeaders(context, contentDispositionString, contentTypeString, urlString); + } + } finally { + // Disconnect the HTTP URL connection. + httpUrlConnection.disconnect(); } - } finally { - // Disconnect the HTTP URL connection. - httpUrlConnection.disconnect(); - } - } catch (Exception exception) { - // Set the formatted file size to indicate a bad URL. - formattedFileSize = context.getString(R.string.invalid_url); + } catch (Exception exception) { + // Set the formatted file size to indicate a bad URL. + formattedFileSize = context.getString(R.string.invalid_url); - // Set the file name according to the URL. - fileNameString = getFileNameFromUrl(context, urlString); + // Set the file name according to the URL. + fileNameString = getFileNameFromUrl(context, urlString, null); + } } // Return the formatted file size and name as a string array. @@ -172,15 +201,21 @@ public class PrepareSaveDialog extends AsyncTask { } // Instantiate the save dialog. - DialogFragment saveDialogFragment = SaveDialog.saveUrl(saveType, urlString, fileStringArray[0], fileStringArray[1], userAgent, cookiesEnabled); + DialogFragment saveDialogFragment = SaveWebpageDialog.saveWebpage(saveType, urlString, fileStringArray[0], fileStringArray[1], userAgent, cookiesEnabled); - // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name. - saveDialogFragment.show(fragmentManager, activity.getString(R.string.save_dialog)); + // Try to show the dialog. Sometimes the window is not active. + try { + // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name. + saveDialogFragment.show(fragmentManager, activity.getString(R.string.save_dialog)); + } catch (Exception exception) { + // Add the dialog to the pending dialog array list. It will be displayed in `onStart()`. + MainWebViewActivity.pendingDialogsArrayList.add(new PendingDialog(saveDialogFragment, activity.getString(R.string.save_dialog))); + } } // Content dispositions can contain other text besides the file name, and they can be in any order. // Elements are separated by semicolons. Sometimes the file names are contained in quotes. - public static String getFileNameFromContentDisposition(Context context, String contentDispositionString, String urlString) { + public static String getFileNameFromHeaders(Context context, String contentDispositionString, String contentTypeString, String urlString) { // Define a file name string. String fileNameString; @@ -208,20 +243,20 @@ public class PrepareSaveDialog extends AsyncTask { // Remove the last character. fileNameString = fileNameString.substring(0, fileNameString.length() - 1); } - } else { // The content disposition does not contain a filename. + } else { // The headers contain no useful information. // Get the file name string from the URL. - fileNameString = getFileNameFromUrl(context, urlString); + fileNameString = getFileNameFromUrl(context, urlString, contentTypeString); } } else { // The content disposition is null. // Get the file name string from the URL. - fileNameString = getFileNameFromUrl(context, urlString); + fileNameString = getFileNameFromUrl(context, urlString, contentTypeString); } // Return the file name string. return fileNameString; } - private static String getFileNameFromUrl(Context context, String urlString) { + private static String getFileNameFromUrl(Context context, String urlString, String contentTypeString) { // Convert the URL string to a URI. Uri uri = Uri.parse(urlString); @@ -231,6 +266,11 @@ public class PrepareSaveDialog extends AsyncTask { // Use a default file name if the last path segment is null. if (lastPathSegment == null) { lastPathSegment = context.getString(R.string.file); + + if (MimeTypeMap.getSingleton().hasMimeType(contentTypeString)) { // The content type contains a MIME type. + // Add the file extension that matches the MIME type. + lastPathSegment = lastPathSegment + "." + MimeTypeMap.getSingleton().getExtensionFromMimeType(contentTypeString); + } } // Return the last path segment as the file name.