]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
Add fallbacks to open alternate download managers. https://redmine.stoutner.com/issue...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / MainWebViewActivity.java
index 6b2d7eb0d8965ff055bc69c128a2a1aad12c8c65..676c4d4813e6a216c158373558311c25490d9cc0 100644 (file)
@@ -48,11 +48,13 @@ import android.net.http.SslError;
 import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.Message;
 import android.preference.PreferenceManager;
 import android.print.PrintDocumentAdapter;
 import android.print.PrintManager;
+import android.provider.DocumentsContract;
 import android.text.Editable;
 import android.text.Spanned;
 import android.text.TextWatcher;
@@ -82,6 +84,7 @@ import android.webkit.WebView;
 import android.webkit.WebViewClient;
 import android.webkit.WebViewDatabase;
 import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
 import android.widget.CursorAdapter;
 import android.widget.EditText;
 import android.widget.FrameLayout;
@@ -94,7 +97,6 @@ import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.appcompat.app.ActionBar;
 import androidx.appcompat.app.ActionBarDrawerToggle;
 import androidx.appcompat.app.AppCompatActivity;
@@ -150,7 +152,11 @@ import com.stoutner.privacybrowser.views.NestedScrollWebView;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -1756,33 +1762,24 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             // Consume the event.
             return true;
-        } else if (menuItemId == R.id.save_image) {  // Save image.
+        } else if (menuItemId == R.id.save_archive) {
             // Instantiate the save dialog.
-            DialogFragment saveImageFragment = SaveWebpageDialog.saveWebpage(SaveWebpageDialog.SAVE_IMAGE, null, null, getString(R.string.webpage_png), null,
+            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.
-            saveImageFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
+            saveArchiveFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
 
-            // Consume the event.
             return true;
-        } else if (menuItemId == R.id.save_archive) {
-            /*  TODO.
-            try {
-                // Create an MHT file.
-                File mhtFile = File.createTempFile("mht_file", ".mht", getCacheDir());
-
-                // Populate the MHT file.
-                currentWebView.saveWebArchive(mhtFile.toString());
-
-                // Check the file length.
-                Log.i("MHT", "MHT file size:  " + mhtFile.length());
-            } catch (Exception exception){
-                Log.i("MHT", "MHT exception:  " + exception.toString());
-            }
+        } 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));
 
+            // Consume the event.
             return true;
         } else if (menuItemId == R.id.add_to_homescreen) {  // Add to homescreen.
             // Instantiate the create home screen shortcut dialog.
@@ -2034,14 +2031,50 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Make it so.
             startActivity(requestsIntent);
         } else if (menuItemId == R.id.downloads) {  // Downloads.
-            // Launch the system Download Manager.
-            Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
+            // Try the default system download manager.
+            try {
+                // Launch the default system Download Manager.
+                Intent defaultDownloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
 
-            // Launch as a new task so that Download Manager and Privacy Browser show as separate windows in the recent tasks list.
-            downloadManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                // Launch as a new task so that the download manager and Privacy Browser show as separate windows in the recent tasks list.
+                defaultDownloadManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-            // Make it so.
-            startActivity(downloadManagerIntent);
+                // Make it so.
+                startActivity(defaultDownloadManagerIntent);
+            } catch (Exception defaultDownloadManagerException) {
+                // Try a generic file manager.
+                try {
+                    // Create a generic file manager intent.
+                    Intent genericFileManagerIntent = new Intent(Intent.ACTION_VIEW);
+
+                    // Open the download directory.
+                    genericFileManagerIntent.setDataAndType(Uri.parse(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString()), DocumentsContract.Document.MIME_TYPE_DIR);
+
+                    // Launch as a new task so that the file manager and Privacy Browser show as separate windows in the recent tasks list.
+                    genericFileManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+                    // Make it so.
+                    startActivity(genericFileManagerIntent);
+                } catch (Exception gengericFileManagerException) {
+                    // Try an alternate file manager.
+                    try {
+                        // Create an alternate file manager intent.
+                        Intent alternateFileManagerIntent = new Intent(Intent.ACTION_VIEW);
+
+                        // Open the download directory.
+                        alternateFileManagerIntent.setDataAndType(Uri.parse(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString()), "resource/folder");
+
+                        // Launch as a new task so that the file manager and Privacy Browser show as separate windows in the recent tasks list.
+                        alternateFileManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+                        // Open the alternate file manager.
+                        startActivity(alternateFileManagerIntent);
+                    } catch (Exception alternateFileManagerException) {
+                        // Display a snackbar.
+                        Snackbar.make(currentWebView, R.string.no_file_manager_detected, Snackbar.LENGTH_INDEFINITE).show();
+                    }
+                }
+            }
         } else if (menuItemId == R.id.domains) {  // Domains.
             // Set the flag to reapply the domain settings on restart when returning from Domain Settings.
             reapplyDomainSettingsOnRestart = true;
@@ -3013,8 +3046,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Remove the incorrect lint warning below that the dialog might be null.
         assert dialog != null;
 
-        // Get a handle for the file name edit text.
+        // Get handles for the views.
         EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext);
+        CheckBox mhtCheckBox = dialog.findViewById(R.id.mht_checkbox);
 
         // Get the file path string.
         String openFilePath = fileNameEditText.getText().toString();
@@ -3022,12 +3056,50 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Apply the domain settings.  This resets the favorite icon and removes any domain settings.
         applyDomainSettings(currentWebView, openFilePath, true, false, false);
 
-        // Open the file.
-        currentWebView.loadUrl(openFilePath);
+        // Open the file according to the type.
+        if (mhtCheckBox.isChecked()) {  // Force opening of an MHT file.
+            try {
+                // Get the MHT file input stream.
+                InputStream mhtFileInputStream = getContentResolver().openInputStream(Uri.parse(openFilePath));
+
+                // Create a temporary MHT file.
+                File temporaryMhtFile = File.createTempFile("temporary_mht_file", ".mht", getCacheDir());
+
+                // Get a file output stream for the temporary MHT file.
+                FileOutputStream temporaryMhtFileOutputStream = new FileOutputStream(temporaryMhtFile);
+
+                // Create a transfer byte array.
+                byte[] transferByteArray = new byte[1024];
+
+                // Create an integer to track the number of bytes read.
+                int bytesRead;
+
+                // Copy the temporary MHT file input stream to the MHT output stream.
+                while ((bytesRead = mhtFileInputStream.read(transferByteArray)) > 0) {
+                    temporaryMhtFileOutputStream.write(transferByteArray, 0, bytesRead);
+                }
+
+                // Flush the temporary MHT file output stream.
+                temporaryMhtFileOutputStream.flush();
+
+                // Close the streams.
+                temporaryMhtFileOutputStream.close();
+                mhtFileInputStream.close();
+
+                // Load the temporary MHT file.
+                currentWebView.loadUrl(temporaryMhtFile.toString());
+            } catch (Exception exception) {
+                // Display a snackbar.
+                Snackbar.make(currentWebView, getString(R.string.error) + "  " + exception.toString(), Snackbar.LENGTH_INDEFINITE).show();
+            }
+        } else {  // Let the WebView handle opening of the file.
+            // Open the file.
+            currentWebView.loadUrl(openFilePath);
+        }
     }
 
     @Override
-    public void onSaveWebpage(int saveType, @Nullable String originalUrlString, DialogFragment dialogFragment) {
+    public void onSaveWebpage(int saveType, @NonNull String originalUrlString, DialogFragment dialogFragment) {
         // Get the dialog.
         Dialog dialog = dialogFragment.getDialog();
 
@@ -3050,7 +3122,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 String saveWebpageUrl;
 
                 // Store the URL.
-                if ((originalUrlString != null) && originalUrlString.startsWith("data:")) {
+                if (originalUrlString.startsWith("data:")) {
                     // Save the original URL.
                     saveWebpageUrl = originalUrlString;
                 } else {
@@ -3062,6 +3134,57 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
                 break;
 
+            case SaveWebpageDialog.SAVE_ARCHIVE:
+                try {
+                    // Create a temporary MHT file.
+                    File temporaryMhtFile = File.createTempFile("temporary_mht_file", ".mht", getCacheDir());
+
+                    // Save the temporary MHT file.
+                    currentWebView.saveWebArchive(temporaryMhtFile.toString(), false, callbackValue -> {
+                        if (callbackValue != null) {  // The temporary MHT file was saved successfully.
+                            try {
+                                // Create a temporary MHT file input stream.
+                                FileInputStream temporaryMhtFileInputStream = new FileInputStream(temporaryMhtFile);
+
+                                // Get an output stream for the save webpage file path.
+                                OutputStream mhtOutputStream = getContentResolver().openOutputStream(Uri.parse(saveWebpageFilePath));
+
+                                // Create a transfer byte array.
+                                byte[] transferByteArray = new byte[1024];
+
+                                // Create an integer to track the number of bytes read.
+                                int bytesRead;
+
+                                // Copy the temporary MHT file input stream to the MHT output stream.
+                                while ((bytesRead = temporaryMhtFileInputStream.read(transferByteArray)) > 0) {
+                                    mhtOutputStream.write(transferByteArray, 0, bytesRead);
+                                }
+
+                                // Close the streams.
+                                mhtOutputStream.close();
+                                temporaryMhtFileInputStream.close();
+
+                                // Display a snackbar.
+                                Snackbar.make(currentWebView, getString(R.string.file_saved) + "  " + currentWebView.getCurrentUrl(), Snackbar.LENGTH_SHORT).show();
+                            } catch (Exception exception) {
+                                // Display a snackbar with the exception.
+                                Snackbar.make(currentWebView, getString(R.string.error_saving_file) + "  " + exception.toString(), Snackbar.LENGTH_INDEFINITE).show();
+                            } finally {
+                                // Delete the temporary MHT file.
+                                //noinspection ResultOfMethodCallIgnored
+                                temporaryMhtFile.delete();
+                            }
+                        } else {  // There was an unspecified error while saving the temporary MHT file.
+                            // Display an error snackbar.
+                            Snackbar.make(currentWebView, getString(R.string.error_saving_file), Snackbar.LENGTH_INDEFINITE).show();
+                        }
+                    });
+                } catch (IOException ioException) {
+                    // Display a snackbar with the IO exception.
+                    Snackbar.make(currentWebView, getString(R.string.error_saving_file) + "  " + ioException.toString(), Snackbar.LENGTH_INDEFINITE).show();
+                }
+                break;
+
             case SaveWebpageDialog.SAVE_IMAGE:
                 // Save the webpage image.
                 new SaveWebpageImage(this, saveWebpageFilePath, currentWebView).execute();
@@ -5096,6 +5219,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Explicitly disable geolocation.
         nestedScrollWebView.getSettings().setGeolocationEnabled(false);
 
+        // Allow loading of file:// URLs.  This is necessary for opening MHT web archives, which are copies into a temporary cache location.
+        nestedScrollWebView.getSettings().setAllowFileAccess(true);
+
         // Create a double-tap gesture detector to toggle full-screen mode.
         GestureDetector doubleTapGestureDetector = new GestureDetector(getApplicationContext(), new GestureDetector.SimpleOnGestureListener() {
             // Override `onDoubleTap()`.  All other events are handled using the default settings.