]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/commitdiff
Simplify the SaveWebpageDialog. https://redmine.stoutner.com/issues/769
authorSoren Stoutner <soren@stoutner.com>
Mon, 22 Nov 2021 19:50:04 +0000 (12:50 -0700)
committerSoren Stoutner <soren@stoutner.com>
Mon, 22 Nov 2021 19:50:04 +0000 (12:50 -0700)
20 files changed:
app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.kt
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/asynctasks/PrepareSaveDialog.java
app/src/main/java/com/stoutner/privacybrowser/asynctasks/SaveAboutVersionImage.java
app/src/main/java/com/stoutner/privacybrowser/asynctasks/SaveUrl.java
app/src/main/java/com/stoutner/privacybrowser/asynctasks/SaveWebpageImage.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveDialog.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveWebpageDialog.kt [deleted file]
app/src/main/java/com/stoutner/privacybrowser/fragments/AboutVersionFragment.kt
app/src/main/res/drawable/cookies_ghosted_day.xml [deleted file]
app/src/main/res/drawable/cookies_ghosted_night.xml [deleted file]
app/src/main/res/layout/save_dialog.xml
app/src/main/res/layout/save_webpage_dialog.xml [deleted file]
app/src/main/res/values-night-v23/styles.xml
app/src/main/res/values-night-v27/styles.xml
app/src/main/res/values-night/styles.xml
app/src/main/res/values-v23/styles.xml
app/src/main/res/values-v27/styles.xml
app/src/main/res/values/attrs.xml
app/src/main/res/values/styles.xml

index e555f1943e062129802e4ceb2f86ca565b4e35b7..71623cd7c3b3e4785639c27be5b143b0ea6a6f7f 100644 (file)
@@ -62,15 +62,15 @@ class LogcatActivity : AppCompatActivity() {
     private lateinit var logcatTextView: TextView
 
     // Define the save logcat activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
-    private val saveLogcatActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument()) { fileNameUri: Uri? ->
+    private val saveLogcatActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument()) { fileUri: Uri? ->
         // Only save the file if the URI is not null, which happens if the user exited the file picker by pressing back.
-        if (fileNameUri != null) {
+        if (fileUri != null) {
             try {
                 // Get the logcat string.
                 val logcatString = logcatTextView.text.toString()
 
                 // Open an output stream.
-                val outputStream = contentResolver.openOutputStream(fileNameUri)!!
+                val outputStream = contentResolver.openOutputStream(fileUri)!!
 
                 // Write the logcat string to the output stream.
                 outputStream.write(logcatString.toByteArray(StandardCharsets.UTF_8))
@@ -78,13 +78,13 @@ class LogcatActivity : AppCompatActivity() {
                 // Close the output stream.
                 outputStream.close()
 
-                // Initialize the file name string from the file name URI last path segment.
-                var fileNameString = fileNameUri.lastPathSegment
+                // Initialize the file name string from the file URI last path segment.
+                var fileNameString = fileUri.lastPathSegment
 
                 // Query the exact file name if the API >= 26.
                 if (Build.VERSION.SDK_INT >= 26) {
                     // Get a cursor from the content resolver.
-                    val contentResolverCursor = contentResolver.query(fileNameUri, null, null, null)!!
+                    val contentResolverCursor = contentResolver.query(fileUri, null, null, null)!!
 
                     // Move to the fist row.
                     contentResolverCursor.moveToFirst()
index 498b034edc4897c9bfd9ad2802f31071603df430..0fd451611a28312b03479e36194e921e89c08878 100644 (file)
@@ -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;
@@ -138,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;
@@ -182,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`.
@@ -212,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()`.
@@ -308,6 +311,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     // Define the class variables.
     private long lastScrollUpdate = 0;
+    private String saveUrlString = "";
 
     // Declare the class views.
     private FrameLayout rootFrameLayout;
@@ -376,6 +380,114 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     private MenuItem optionsFontSizeMenuItem;
     private MenuItem optionsAddOrEditDomainMenuItem;
 
+    // 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<String> saveUrlActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(),
+            new ActivityResultCallback<Uri>() {
+                @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<String> saveWebpageArchiveActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(),
+            new ActivityResultCallback<Uri>() {
+                @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<String> saveWebpageImageActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(),
+            new ActivityResultCallback<Uri>() {
+                @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
@@ -383,6 +495,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // 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.
@@ -1743,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;
@@ -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.
@@ -5383,7 +5389,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 String fileNameString = PrepareSaveDialog.getFileNameFromHeaders(this, contentDisposition, mimetype, downloadUrl);
 
                 // 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());
 
                 // 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.
index 28c3e65606f366cb5d8121785100ceef4333469d..c767def1fd0b99be3198ea7928406c646185bed3 100644 (file)
@@ -32,7 +32,7 @@ import androidx.fragment.app.FragmentManager;
 import com.stoutner.privacybrowser.R;
 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
 import com.stoutner.privacybrowser.dataclasses.PendingDialog;
-import com.stoutner.privacybrowser.dialogs.SaveWebpageDialog;
+import com.stoutner.privacybrowser.dialogs.SaveDialog;
 import com.stoutner.privacybrowser.helpers.ProxyHelper;
 
 import java.lang.ref.WeakReference;
@@ -48,20 +48,18 @@ public class PrepareSaveDialog extends AsyncTask<String, Void, String[]> {
     private final WeakReference<FragmentManager> fragmentManagerWeakReference;
 
     // Define the class variables.
-    private final int saveType;
     private final String userAgent;
     private final boolean cookiesEnabled;
     private String urlString;
 
     // The public constructor.
-    public PrepareSaveDialog(Activity activity, Context context, FragmentManager fragmentManager, int saveType, String userAgent, boolean cookiesEnabled) {
+    public PrepareSaveDialog(Activity activity, Context context, FragmentManager fragmentManager, String userAgent, boolean cookiesEnabled) {
         // Populate the weak references.
         activityWeakReference = new WeakReference<>(activity);
         contextWeakReference = new WeakReference<>(context);
         fragmentManagerWeakReference = new WeakReference<>(fragmentManager);
 
         // Store the class variables.
-        this.saveType = saveType;
         this.userAgent = userAgent;
         this.cookiesEnabled = cookiesEnabled;
     }
@@ -90,7 +88,7 @@ public class PrepareSaveDialog extends AsyncTask<String, Void, String[]> {
             // Remove `data:` from the beginning of the URL.
             String urlWithoutData = urlString.substring(5);
 
-            // Get the URL MIME type, which end with a `;`.
+            // Get the URL MIME type, which ends with a `;`.
             String urlMimeType = urlWithoutData.substring(0, urlWithoutData.indexOf(";"));
 
             // Get the Base64 data, which begins after a `,`.
@@ -201,7 +199,7 @@ public class PrepareSaveDialog extends AsyncTask<String, Void, String[]> {
         }
 
         // Instantiate the save dialog.
-        DialogFragment saveDialogFragment = SaveWebpageDialog.saveWebpage(saveType, urlString, fileStringArray[0], fileStringArray[1], userAgent, cookiesEnabled);
+        DialogFragment saveDialogFragment = SaveDialog.saveUrl(urlString, fileStringArray[0], fileStringArray[1], userAgent, cookiesEnabled);
 
         // Try to show the dialog.  Sometimes the window is not active.
         try {
index 1c15bc9c56c9dea22c4097509818d5eb1e2ec7e8..1fef50dcd77005271ee30422ab3847809f066145 100644 (file)
@@ -48,22 +48,22 @@ public class SaveAboutVersionImage extends AsyncTask<Void, Void, String> {
     // Declare the class variables.
     private Snackbar savingImageSnackbar;
     private Bitmap aboutVersionBitmap;
-    private final Uri fileNameUri;
+    private final Uri fileUri;
     private final String fileNameString;
 
     // The public constructor.
-    public SaveAboutVersionImage(Activity activity, Uri fileNameUri, LinearLayout aboutVersionLinearLayout) {
+    public SaveAboutVersionImage(Activity activity, Uri fileUri, LinearLayout aboutVersionLinearLayout) {
         // Populate the weak references.
         activityWeakReference = new WeakReference<>(activity);
         aboutVersionLinearLayoutWeakReference = new WeakReference<>(aboutVersionLinearLayout);
 
         // Store the class variables.
-        this.fileNameUri = fileNameUri;
+        this.fileUri = fileUri;
 
         // Query the exact file name if the API >= 26.
         if (Build.VERSION.SDK_INT >= 26) {
             // Get a cursor from the content resolver.
-            Cursor contentResolverCursor = activity.getContentResolver().query(fileNameUri, null, null, null);
+            Cursor contentResolverCursor = activity.getContentResolver().query(fileUri, null, null, null);
 
             // Move to the first row.
             contentResolverCursor.moveToFirst();
@@ -75,7 +75,7 @@ public class SaveAboutVersionImage extends AsyncTask<Void, Void, String> {
             contentResolverCursor.close();
         } else {
             // Use the URI last path segment as the file name string.
-            fileNameString = fileNameUri.getLastPathSegment();
+            fileNameString = fileUri.getLastPathSegment();
         }
     }
 
@@ -129,7 +129,7 @@ public class SaveAboutVersionImage extends AsyncTask<Void, Void, String> {
 
         try {
             // Open an output stream.
-            OutputStream outputStream = activity.getContentResolver().openOutputStream(fileNameUri);
+            OutputStream outputStream = activity.getContentResolver().openOutputStream(fileUri);
 
             // Write the webpage image to the image file.
             aboutVersionByteArrayOutputStream.writeTo(outputStream);
index c93070951eeb352d380b90590fac9d53f230d4b0..6a4671bc7e20b478c9ed4d014efaf0cd3f86dcff 100644 (file)
@@ -21,8 +21,11 @@ package com.stoutner.privacybrowser.asynctasks;
 
 import android.app.Activity;
 import android.content.Context;
+import android.database.Cursor;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Build;
+import android.provider.OpenableColumns;
 import android.util.Base64;
 import android.webkit.CookieManager;
 
@@ -41,32 +44,50 @@ import java.net.URL;
 import java.text.NumberFormat;
 
 public class SaveUrl extends AsyncTask<String, Long, String> {
-    // Define a weak references.
+    // Declare the weak references.
     private final WeakReference<Context> contextWeakReference;
     private final WeakReference<Activity> activityWeakReference;
 
     // Define a success string constant.
     private final String SUCCESS = "Success";
 
-    // Define the class variables.
-    private final String filePathString;
+    // Declare the class variables.
+    private final Uri fileUri;
     private final String userAgent;
     private final boolean cookiesEnabled;
     private Snackbar savingFileSnackbar;
     private long fileSize;
     private String formattedFileSize;
-    private String urlString = "";
+    private final String fileNameString;
 
     // The public constructor.
-    public SaveUrl(Context context, Activity activity, String filePathString, String userAgent, boolean cookiesEnabled) {
+    public SaveUrl(Context context, Activity activity, Uri fileUri, String userAgent, boolean cookiesEnabled) {
         // Populate weak references to the calling context and activity.
         contextWeakReference = new WeakReference<>(context);
         activityWeakReference = new WeakReference<>(activity);
 
         // Store the class variables.
-        this.filePathString = filePathString;
+        this.fileUri = fileUri;
         this.userAgent = userAgent;
         this.cookiesEnabled = cookiesEnabled;
+
+        // Query the exact file name if the API >= 26.
+        if (Build.VERSION.SDK_INT >= 26) {
+            // Get a cursor from the content resolver.
+            Cursor contentResolverCursor = activity.getContentResolver().query(fileUri, null, null, null);
+
+            // Move to the first row.
+            contentResolverCursor.moveToFirst();
+
+            // Get the file name from the cursor.
+            fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
+
+            // Close the cursor.
+            contentResolverCursor.close();
+        } else {
+            // Use the file URI last path segment as the file name string.
+            fileNameString = fileUri.getLastPathSegment();
+        }
     }
 
     // `onPreExecute()` operates on the UI thread.
@@ -84,7 +105,7 @@ public class SaveUrl extends AsyncTask<String, Long, String> {
         NoSwipeViewPager noSwipeViewPager = activity.findViewById(R.id.webviewpager);
 
         // Create a saving file snackbar.
-        savingFileSnackbar = Snackbar.make(noSwipeViewPager, activity.getString(R.string.saving_file) + "  0%  -  " + urlString, Snackbar.LENGTH_INDEFINITE);
+        savingFileSnackbar = Snackbar.make(noSwipeViewPager, activity.getString(R.string.saving_file) + "  0%  -  " + fileNameString, Snackbar.LENGTH_INDEFINITE);
 
         // Display the saving file snackbar.
         savingFileSnackbar.show();
@@ -105,11 +126,11 @@ public class SaveUrl extends AsyncTask<String, Long, String> {
         String saveDisposition = SUCCESS;
 
         // Get the URL string.
-        urlString = urlToSave[0];
+        String urlString = urlToSave[0];
 
         try {
             // Open an output stream.
-            OutputStream outputStream = activity.getContentResolver().openOutputStream(Uri.parse(filePathString));
+            OutputStream outputStream = activity.getContentResolver().openOutputStream(fileUri);
 
             // Save the URL.
             if (urlString.startsWith("data:")) {  // The URL contains the entire data of an image.
@@ -227,14 +248,14 @@ public class SaveUrl extends AsyncTask<String, Long, String> {
         // Check to see if the file size is known.
         if (fileSize == -1) {  // The size of the download file is not known.
             // Update the snackbar.
-            savingFileSnackbar.setText(activity.getString(R.string.saving_file) + "  " + formattedNumberOfBytesDownloaded + " " + activity.getString(R.string.bytes) + "  -  " + urlString);
+            savingFileSnackbar.setText(activity.getString(R.string.saving_file) + "  " + formattedNumberOfBytesDownloaded + " " + activity.getString(R.string.bytes) + "  -  " + fileNameString);
         } else {  // The size of the download file is known.
             // Calculate the download percentage.
             long downloadPercentage = (numberOfBytesDownloaded[0] * 100) / fileSize;
 
             // Update the snackbar.
-            savingFileSnackbar.setText(activity.getString(R.string.saving_file) + "  " + downloadPercentage + "%  -  " + formattedNumberOfBytesDownloaded + " " + activity.getString(R.string.bytes) + " / " + formattedFileSize + " " +
-                    activity.getString(R.string.bytes) + "  -  " + urlString);
+            savingFileSnackbar.setText(activity.getString(R.string.saving_file) + "  " + downloadPercentage + "%  -  " + formattedNumberOfBytesDownloaded + " " + activity.getString(R.string.bytes) + " / " +
+                    formattedFileSize + " " + activity.getString(R.string.bytes) + "  -  " + fileNameString);
         }
     }
 
@@ -258,7 +279,7 @@ public class SaveUrl extends AsyncTask<String, Long, String> {
         // Display a save disposition snackbar.
         if (saveDisposition.equals(SUCCESS)) {
             // Display the file saved snackbar.
-            Snackbar.make(noSwipeViewPager, activity.getString(R.string.file_saved) + "  " + urlString, Snackbar.LENGTH_LONG).show();
+            Snackbar.make(noSwipeViewPager, activity.getString(R.string.file_saved) + "  " + fileNameString, Snackbar.LENGTH_LONG).show();
         } else {
             // Display the file saving error.
             Snackbar.make(noSwipeViewPager, activity.getString(R.string.error_saving_file) + "  " + saveDisposition, Snackbar.LENGTH_INDEFINITE).show();
index e44f5e6d028535425223f28bb632b01bff0e4ed4..f4b43d6c2d00d89cb1bef464e15b6bb3e502a1e4 100644 (file)
 package com.stoutner.privacybrowser.asynctasks;
 
 import android.app.Activity;
+import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Build;
+import android.provider.OpenableColumns;
 
 import com.google.android.material.snackbar.Snackbar;
 
@@ -45,16 +48,35 @@ public class SaveWebpageImage extends AsyncTask<Void, Void, String> {
     // Declare the class variables.
     private Snackbar savingImageSnackbar;
     private Bitmap webpageBitmap;
-    private final String filePathString;
+    private final Uri fileUri;
+    private final String fileNameString;
 
     // The public constructor.
-    public SaveWebpageImage(Activity activity, String filePathString, NestedScrollWebView nestedScrollWebView) {
+    public SaveWebpageImage(Activity activity, Uri fileUri, NestedScrollWebView nestedScrollWebView) {
         // Populate the weak references.
         activityWeakReference = new WeakReference<>(activity);
         nestedScrollWebViewWeakReference = new WeakReference<>(nestedScrollWebView);
 
         // Populate the class variables.
-        this.filePathString = filePathString;
+        this.fileUri = fileUri;
+
+        // Query the exact file name if the API >= 26.
+        if (Build.VERSION.SDK_INT >= 26) {
+            // Get a cursor from the content resolver.
+            Cursor contentResolverCursor = activity.getContentResolver().query(fileUri, null, null, null);
+
+            // Move to the first row.
+            contentResolverCursor.moveToFirst();
+
+            // Get the file name from the cursor.
+            fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
+
+            // Close the cursor.
+            contentResolverCursor.close();
+        } else {
+            // Use the file URI last path segment as the file name string.
+            fileNameString = fileUri.getLastPathSegment();
+        }
     }
 
     // `onPreExecute()` operates on the UI thread.
@@ -70,7 +92,7 @@ public class SaveWebpageImage extends AsyncTask<Void, Void, String> {
         }
 
         // Create a saving image snackbar.
-        savingImageSnackbar = Snackbar.make(nestedScrollWebView, activity.getString(R.string.processing_image) + "  " + nestedScrollWebView.getCurrentUrl(), Snackbar.LENGTH_INDEFINITE);
+        savingImageSnackbar = Snackbar.make(nestedScrollWebView, activity.getString(R.string.processing_image) + "  " + fileNameString, Snackbar.LENGTH_INDEFINITE);
 
         // Display the saving image snackbar.
         savingImageSnackbar.show();
@@ -106,7 +128,7 @@ public class SaveWebpageImage extends AsyncTask<Void, Void, String> {
 
         try {
             // Create an image file output stream.
-            OutputStream imageFileOutputStream = activity.getContentResolver().openOutputStream(Uri.parse(filePathString));
+            OutputStream imageFileOutputStream = activity.getContentResolver().openOutputStream(fileUri);
 
             // Write the webpage image to the image file.
             webpageByteArrayOutputStream.writeTo(imageFileOutputStream);
@@ -137,7 +159,7 @@ public class SaveWebpageImage extends AsyncTask<Void, Void, String> {
         // Display a file creation disposition snackbar.
         if (fileCreationDisposition.equals(SUCCESS)) {
             // Display the image saved snackbar.
-            Snackbar.make(nestedScrollWebView, activity.getString(R.string.image_saved) + "  " + nestedScrollWebView.getCurrentUrl(), Snackbar.LENGTH_SHORT).show();
+            Snackbar.make(nestedScrollWebView, activity.getString(R.string.image_saved) + "  " + fileNameString, Snackbar.LENGTH_SHORT).show();
         } else {
             // Display the file saving error.
             Snackbar.make(nestedScrollWebView, activity.getString(R.string.error_saving_file) + "  " + fileCreationDisposition, Snackbar.LENGTH_INDEFINITE).show();
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveDialog.kt
new file mode 100644 (file)
index 0000000..f681e8d
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright © 2019-2021 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.dialogs
+
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import android.os.AsyncTask
+import android.os.Bundle
+import android.text.Editable
+import android.text.InputType
+import android.text.TextWatcher
+import android.view.WindowManager
+import android.widget.EditText
+import android.widget.TextView
+
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import androidx.preference.PreferenceManager
+
+import com.stoutner.privacybrowser.R
+import com.stoutner.privacybrowser.asynctasks.GetUrlSize
+
+// Define the class constants.
+private const val URL_STRING = "url_string"
+private const val FILE_SIZE_STRING = "file_size_string"
+private const val FILE_NAME_STRING = "file_name_string"
+private const val USER_AGENT_STRING = "user_agent_string"
+private const val COOKIES_ENABLED = "cookies_enabled"
+
+class SaveDialog : DialogFragment() {
+    // Declare the class variables.
+    private lateinit var saveListener: SaveListener
+
+    // Define the class variables.
+    private var getUrlSize: AsyncTask<*, *, *>? = null
+
+    // The public interface is used to send information back to the parent activity.
+    interface SaveListener {
+        fun onSaveUrl(originalUrlString: String, fileNameString: String, dialogFragment: DialogFragment)
+    }
+
+    override fun onAttach(context: Context) {
+        // Run the default commands.
+        super.onAttach(context)
+
+        // Get a handle for the save webpage listener from the launching context.
+        saveListener = context as SaveListener
+    }
+
+    companion object {
+        // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin.
+        @JvmStatic
+        fun saveUrl(urlString: String, fileSizeString: String, fileNameString: String, userAgentString: String, cookiesEnabled: Boolean): SaveDialog {
+            // Create an arguments bundle.
+            val argumentsBundle = Bundle()
+
+            // Store the arguments in the bundle.
+            argumentsBundle.putString(URL_STRING, urlString)
+            argumentsBundle.putString(FILE_SIZE_STRING, fileSizeString)
+            argumentsBundle.putString(FILE_NAME_STRING, fileNameString)
+            argumentsBundle.putString(USER_AGENT_STRING, userAgentString)
+            argumentsBundle.putBoolean(COOKIES_ENABLED, cookiesEnabled)
+
+            // Create a new instance of the save webpage dialog.
+            val saveDialog = SaveDialog()
+
+            // Add the arguments bundle to the new dialog.
+            saveDialog.arguments = argumentsBundle
+
+            // Return the new dialog.
+            return saveDialog
+        }
+    }
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        // Get the arguments from the bundle.
+        val originalUrlString = requireArguments().getString(URL_STRING)!!
+        val fileSizeString = requireArguments().getString(FILE_SIZE_STRING)!!
+        val fileNameString = requireArguments().getString(FILE_NAME_STRING)!!
+        val userAgentString = requireArguments().getString(USER_AGENT_STRING)!!
+        val cookiesEnabled = requireArguments().getBoolean(COOKIES_ENABLED)
+
+        // Use an alert dialog builder to create the alert dialog.
+        val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
+
+        // Set the title.
+        dialogBuilder.setTitle(R.string.save_url)
+
+        // Set the icon according to the theme.
+        dialogBuilder.setIconAttribute(R.attr.copyBlueIcon)
+
+        // Set the view.
+        dialogBuilder.setView(R.layout.save_dialog)
+
+        // Set the cancel button listener.  Using `null` as the listener closes the dialog without doing anything else.
+        dialogBuilder.setNegativeButton(R.string.cancel, null)
+
+        // Set the save button listener.
+        dialogBuilder.setPositiveButton(R.string.save) { _: DialogInterface, _: Int ->
+            // Return the dialog fragment to the parent activity.
+            saveListener.onSaveUrl(originalUrlString, fileNameString, this)
+        }
+
+        // Create an alert dialog from the builder.
+        val alertDialog = dialogBuilder.create()
+
+        // Get a handle for the shared preferences.
+        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+
+        // Get the screenshot preference.
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
+
+        // Disable screenshots if not allowed.
+        if (!allowScreenshots) {
+            alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
+        }
+
+        // The alert dialog must be shown before items in the layout can be modified.
+        alertDialog.show()
+
+        // Get handles for the layout items.
+        val urlEditText = alertDialog.findViewById<EditText>(R.id.url_edittext)!!
+        val fileSizeTextView = alertDialog.findViewById<TextView>(R.id.file_size_textview)!!
+        val saveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
+
+        // Set the file size text view.
+        fileSizeTextView.text = fileSizeString
+
+        // Populate the URL edit text according to the type.  This must be done before the text change listener is created below so that the file size isn't requested again.
+        if (originalUrlString.startsWith("data:")) {  // The URL contains the entire data of an image.
+            // Get a substring of the data URL with the first 100 characters.  Otherwise, the user interface will freeze while trying to layout the edit text.
+            val urlSubstring = originalUrlString.substring(0, 100) + "…"
+
+            // Populate the URL edit text with the truncated URL.
+            urlEditText.setText(urlSubstring)
+
+            // Disable the editing of the URL edit text.
+            urlEditText.inputType = InputType.TYPE_NULL
+        } else {  // The URL contains a reference to the location of the data.
+            // Populate the URL edit text with the full URL.
+            urlEditText.setText(originalUrlString)
+        }
+
+        // Update the file size when the URL changes.
+        urlEditText.addTextChangedListener(object : TextWatcher {
+            override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
+                // Do nothing.
+            }
+
+            override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
+                // Do nothing.
+            }
+
+            override fun afterTextChanged(editable: Editable) {
+                // Cancel the get URL size AsyncTask if it is running.
+                if (getUrlSize != null) {
+                    getUrlSize!!.cancel(true)
+                }
+
+                // Get the current URL to save.
+                val urlToSave = urlEditText.text.toString()
+
+                // Wipe the file size text view.
+                fileSizeTextView.text = ""
+
+                // Get the file size for the current URL.
+                getUrlSize = GetUrlSize(context, alertDialog, userAgentString, cookiesEnabled).execute(urlToSave)
+
+                // Enable the save button if the URL is populated.
+                saveButton.isEnabled = urlToSave.isNotEmpty()
+            }
+        })
+
+        // Return the alert dialog.
+        return alertDialog
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveWebpageDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveWebpageDialog.kt
deleted file mode 100644 (file)
index 3402d4f..0000000
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Copyright © 2019-2021 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
- *
- * Privacy Browser is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Privacy Browser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.dialogs
-
-import android.app.Dialog
-import android.content.Context
-import android.content.DialogInterface
-import android.content.Intent
-import android.net.Uri
-import android.os.AsyncTask
-import android.os.Bundle
-import android.text.Editable
-import android.text.InputType
-import android.text.TextWatcher
-import android.view.View
-import android.view.WindowManager
-import android.widget.Button
-import android.widget.EditText
-import android.widget.TextView
-
-import androidx.appcompat.app.AlertDialog
-import androidx.fragment.app.DialogFragment
-import androidx.preference.PreferenceManager
-
-import com.google.android.material.textfield.TextInputLayout
-
-import com.stoutner.privacybrowser.R
-import com.stoutner.privacybrowser.activities.MainWebViewActivity
-import com.stoutner.privacybrowser.asynctasks.GetUrlSize
-
-// Define the class constants.
-private const val SAVE_TYPE = "save_type"
-private const val URL_STRING = "url_string"
-private const val FILE_SIZE_STRING = "file_size_string"
-private const val FILE_NAME_STRING = "file_name_string"
-private const val USER_AGENT_STRING = "user_agent_string"
-private const val COOKIES_ENABLED = "cookies_enabled"
-
-class SaveWebpageDialog : DialogFragment() {
-    // Declare the class variables.
-    private lateinit var saveWebpageListener: SaveWebpageListener
-
-    // Define the class variables.
-    private var getUrlSize: AsyncTask<*, *, *>? = null
-
-    // The public interface is used to send information back to the parent activity.
-    interface SaveWebpageListener {
-        fun onSaveWebpage(saveType: Int, originalUrlString: String, dialogFragment: DialogFragment)
-    }
-
-    override fun onAttach(context: Context) {
-        // Run the default commands.
-        super.onAttach(context)
-
-        // Get a handle for the save webpage listener from the launching context.
-        saveWebpageListener = context as SaveWebpageListener
-    }
-
-    companion object {
-        // Define the companion object constants.  These can be moved to class constants once all of the code has transitioned to Kotlin.
-        const val SAVE_URL = 0
-        const val SAVE_ARCHIVE = 1
-        const val SAVE_IMAGE = 2
-
-        // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin.
-        @JvmStatic
-        fun saveWebpage(saveType: Int, urlString: String, fileSizeString: String?, fileNameString: String?, userAgentString: String?, cookiesEnabled: Boolean): SaveWebpageDialog {
-            // Create an arguments bundle.
-            val argumentsBundle = Bundle()
-
-            // Store the arguments in the bundle.
-            argumentsBundle.putInt(SAVE_TYPE, saveType)
-            argumentsBundle.putString(URL_STRING, urlString)
-            argumentsBundle.putString(FILE_SIZE_STRING, fileSizeString)
-            argumentsBundle.putString(FILE_NAME_STRING, fileNameString)
-            argumentsBundle.putString(USER_AGENT_STRING, userAgentString)
-            argumentsBundle.putBoolean(COOKIES_ENABLED, cookiesEnabled)
-
-            // Create a new instance of the save webpage dialog.
-            val saveWebpageDialog = SaveWebpageDialog()
-
-            // Add the arguments bundle to the new dialog.
-            saveWebpageDialog.arguments = argumentsBundle
-
-            // Return the new dialog.
-            return saveWebpageDialog
-        }
-    }
-
-    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
-        // Get the arguments from the bundle.
-        val saveType = requireArguments().getInt(SAVE_TYPE)
-        val originalUrlString = requireArguments().getString(URL_STRING)!!
-        val fileSizeString = requireArguments().getString(FILE_SIZE_STRING)
-        var fileNameString = requireArguments().getString(FILE_NAME_STRING)
-        val userAgentString = requireArguments().getString(USER_AGENT_STRING)
-        val cookiesEnabled = requireArguments().getBoolean(COOKIES_ENABLED)
-
-        // Use an alert dialog builder to create the alert dialog.
-        val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
-
-        // Configure the dialog according to the save type.
-        when (saveType) {
-            SAVE_URL -> {
-                // Set the title.
-                dialogBuilder.setTitle(R.string.save_url)
-
-                // Set the icon according to the theme.
-                dialogBuilder.setIconAttribute(R.attr.copyBlueIcon)
-            }
-
-            SAVE_ARCHIVE -> {
-                // Set the title.
-                dialogBuilder.setTitle(R.string.save_archive)
-
-                // Set the icon according to the theme.
-                dialogBuilder.setIconAttribute(R.attr.domStorageBlueIcon)
-
-                // Convert the URL to a URI.
-                val uri = Uri.parse(originalUrlString)
-
-                // Build a file name string based on the host from the URI.
-                fileNameString = uri.host + ".mht"
-            }
-
-            SAVE_IMAGE -> {
-                // Set the title.
-                dialogBuilder.setTitle(R.string.save_image)
-
-                // Set the icon according to the theme.
-                dialogBuilder.setIconAttribute(R.attr.imagesBlueIcon)
-
-                // Convert the URL to a URI.
-                val uri = Uri.parse(originalUrlString)
-
-                // Build a file name string based on the host from the URI.
-                fileNameString = uri.host + ".png"
-            }
-        }
-
-        // Set the view.
-        dialogBuilder.setView(R.layout.save_webpage_dialog)
-
-        // Set the cancel button listener.  Using `null` as the listener closes the dialog without doing anything else.
-        dialogBuilder.setNegativeButton(R.string.cancel, null)
-
-        // Set the save button listener.
-        dialogBuilder.setPositiveButton(R.string.save) { _: DialogInterface, _: Int ->
-            // Return the dialog fragment to the parent activity.
-            saveWebpageListener.onSaveWebpage(saveType, originalUrlString, this)
-        }
-
-        // Create an alert dialog from the builder.
-        val alertDialog = dialogBuilder.create()
-
-        // Get a handle for the shared preferences.
-        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
-
-        // Get the screenshot preference.
-        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
-
-        // Disable screenshots if not allowed.
-        if (!allowScreenshots) {
-            alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
-        }
-
-        // The alert dialog must be shown before items in the layout can be modified.
-        alertDialog.show()
-
-        // Get handles for the layout items.
-        val urlTextInputLayout = alertDialog.findViewById<TextInputLayout>(R.id.url_textinputlayout)!!
-        val urlEditText = alertDialog.findViewById<EditText>(R.id.url_edittext)!!
-        val fileNameEditText = alertDialog.findViewById<EditText>(R.id.file_name_edittext)!!
-        val browseButton = alertDialog.findViewById<Button>(R.id.browse_button)!!
-        val fileSizeTextView = alertDialog.findViewById<TextView>(R.id.file_size_textview)!!
-        val saveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
-
-        // Set the file size text view.
-        fileSizeTextView.text = fileSizeString
-
-        // Modify the layout based on the save type.
-        if (saveType == SAVE_URL) {  // A URL is being saved.
-            // Populate the URL edit text according to the type.  This must be done before the text change listener is created below so that the file size isn't requested again.
-            if (originalUrlString.startsWith("data:")) {  // The URL contains the entire data of an image.
-                // Get a substring of the data URL with the first 100 characters.  Otherwise, the user interface will freeze while trying to layout the edit text.
-                val urlSubstring = originalUrlString.substring(0, 100) + "…"
-
-                // Populate the URL edit text with the truncated URL.
-                urlEditText.setText(urlSubstring)
-
-                // Disable the editing of the URL edit text.
-                urlEditText.inputType = InputType.TYPE_NULL
-            } else {  // The URL contains a reference to the location of the data.
-                // Populate the URL edit text with the full URL.
-                urlEditText.setText(originalUrlString)
-            }
-
-            // Update the file size and the status of the save button when the URL changes.
-            urlEditText.addTextChangedListener(object : TextWatcher {
-                override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
-                    // Do nothing.
-                }
-
-                override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
-                    // Do nothing.
-                }
-
-                override fun afterTextChanged(editable: Editable) {
-                    // Cancel the get URL size AsyncTask if it is running.
-                    if (getUrlSize != null) {
-                        getUrlSize!!.cancel(true)
-                    }
-
-                    // Get the current URL to save.
-                    val urlToSave = urlEditText.text.toString()
-
-                    // Wipe the file size text view.
-                    fileSizeTextView.text = ""
-
-                    // Get the file size for the current URL.
-                    getUrlSize = GetUrlSize(context, alertDialog, userAgentString, cookiesEnabled).execute(urlToSave)
-
-                    // Enable the save button if the URL and file name are populated.
-                    saveButton.isEnabled = urlToSave.isNotEmpty() && fileNameEditText.text.toString().isNotEmpty()
-                }
-            })
-        } else {  // An archive or an image is being saved.
-            // Hide the URL edit text and the file size text view.
-            urlTextInputLayout.visibility = View.GONE
-            fileSizeTextView.visibility = View.GONE
-        }
-
-        // Initially disable the save button.
-        saveButton.isEnabled = false
-
-        // Update the status of the save button when the file name changes.
-        fileNameEditText.addTextChangedListener(object : TextWatcher {
-            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
-                // Do nothing.
-            }
-
-            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
-                // Do nothing.
-            }
-
-            override fun afterTextChanged(s: Editable) {
-                // Enable the save button based on the save type.
-                if (saveType == SAVE_URL) {  // A URL is being saved.
-                    // Enable the save button if the file name and the URL are populated.
-                    saveButton.isEnabled = fileNameEditText.text.toString().isNotEmpty() && urlEditText.text.toString().isNotEmpty()
-                } else {  // An archive or an image is being saved.
-                    // Enable the save button if the file name is populated.
-                    saveButton.isEnabled = fileNameEditText.text.toString().isNotEmpty()
-                }
-            }
-        })
-
-        // Handle clicks on the browse button.
-        browseButton.setOnClickListener {
-            // Create the file picker intent.
-            val browseIntent = Intent(Intent.ACTION_CREATE_DOCUMENT)
-
-            // Set the intent MIME type to include all files so that everything is visible.
-            browseIntent.type = "*/*"
-
-            // Set the initial file name according to the type.
-            browseIntent.putExtra(Intent.EXTRA_TITLE, fileNameString)
-
-            // Request a file that can be opened.
-            browseIntent.addCategory(Intent.CATEGORY_OPENABLE)
-
-            // Start the file picker.  This must be started under `activity` so that the request code is returned correctly.
-            requireActivity().startActivityForResult(browseIntent, MainWebViewActivity.BROWSE_SAVE_WEBPAGE_REQUEST_CODE)
-        }
-
-        // Return the alert dialog.
-        return alertDialog
-    }
-}
\ No newline at end of file
index 4a9869c44dc0e6715a703e8d1228a9d25022477f..2e99ff2ef76776416dcd2ab6434587364309bd79 100644 (file)
@@ -154,15 +154,15 @@ class AboutVersionFragment : Fragment() {
     }
 
     // Define the save about version text activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
-    private val saveAboutVersionTextActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument()) { fileNameUri: Uri? ->
+    private val saveAboutVersionTextActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument()) { fileUri: Uri? ->
         // Only save the file if the URI is not null, which happens if the user exited the file picker by pressing back.
-        if (fileNameUri != null) {
+        if (fileUri != null) {
             try {
                 // Get the about version string.
                 val aboutVersionString = getAboutVersionString()
 
                 // Open an output stream.
-                val outputStream = requireActivity().contentResolver.openOutputStream(fileNameUri)!!
+                val outputStream = requireActivity().contentResolver.openOutputStream(fileUri)!!
 
                 // Write the about version string to the output stream.
                 outputStream.write(aboutVersionString.toByteArray(StandardCharsets.UTF_8))
@@ -170,13 +170,13 @@ class AboutVersionFragment : Fragment() {
                 // Close the output stream.
                 outputStream.close()
 
-                // Initialize the file name string from the file name URI last path segment.
-                var fileNameString = fileNameUri.lastPathSegment
+                // Initialize the file name string from the file URI last path segment.
+                var fileNameString = fileUri.lastPathSegment
 
                 // Query the exact file name if the API >= 26.
                 if (Build.VERSION.SDK_INT >= 26) {
                     // Get a cursor from the content resolver.
-                    val contentResolverCursor = requireActivity().contentResolver.query(fileNameUri, null, null, null)!!
+                    val contentResolverCursor = requireActivity().contentResolver.query(fileUri, null, null, null)!!
 
                     // Move to the first row.
                     contentResolverCursor.moveToFirst()
@@ -198,11 +198,11 @@ class AboutVersionFragment : Fragment() {
     }
 
     // Define the save about version image activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
-    private val saveAboutVersionImageActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument()) { fileNameUri: Uri? ->
+    private val saveAboutVersionImageActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument()) { fileUri: Uri? ->
         // Only save the file if the URI is not null, which happens if the user exited the file picker by pressing back.
-        if (fileNameUri != null) {
+        if (fileUri != null) {
             // Save the about version image.
-            SaveAboutVersionImage(requireActivity(), fileNameUri, aboutVersionLayout.findViewById(R.id.about_version_linearlayout)).execute()
+            SaveAboutVersionImage(requireActivity(), fileUri, aboutVersionLayout.findViewById(R.id.about_version_linearlayout)).execute()
         }
     }
 
diff --git a/app/src/main/res/drawable/cookies_ghosted_day.xml b/app/src/main/res/drawable/cookies_ghosted_day.xml
deleted file mode 100644 (file)
index 692920b..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<!-- This file was created by Google and downloaded from <https://materialdesignicons.com/icon/cookie>.  It is released under the Apache License 2.0. -->
-
-<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true"` not applying to API < 21. -->
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:height="26dp"
-    android:width="26dp"
-    android:viewportHeight="24"
-    android:viewportWidth="24"
-    android:autoMirrored="true"
-    tools:ignore="VectorRaster" >
-
-    <!-- A hard coded color must be used until API >= 21.  Then `@color` or `?attr/colorControlNormal` may be used instead. -->
-    <path
-        android:fillColor="#FFB7B7B7"
-        android:pathData="M12,3A9,9 0 0,0 3,12A9,9 0 0,0 12,21A9,9 0 0,0 21,12C21,11.5 20.96,11 20.87,10.5C20.6,10 20,10 20,10H18V9C18,8 17,8 17,8H15V7C15,6 14,6 14,6H13V4C13,3 12,3 12,3M9.5,6A1.5,1.5 0 0,1 11,7.5A1.5,1.5 0 0,1 9.5,9A1.5,1.5 0 0,1 8,7.5A1.5,1.5 0 0,1 9.5,6M6.5,10A1.5,1.5 0 0,1 8,11.5A1.5,1.5 0 0,1 6.5,13A1.5,1.5 0 0,1 5,11.5A1.5,1.5 0 0,1 6.5,10M11.5,11A1.5,1.5 0 0,1 13,12.5A1.5,1.5 0 0,1 11.5,14A1.5,1.5 0 0,1 10,12.5A1.5,1.5 0 0,1 11.5,11M16.5,13A1.5,1.5 0 0,1 18,14.5A1.5,1.5 0 0,1 16.5,16H16.5A1.5,1.5 0 0,1 15,14.5H15A1.5,1.5 0 0,1 16.5,13M11,16A1.5,1.5 0 0,1 12.5,17.5A1.5,1.5 0 0,1 11,19A1.5,1.5 0 0,1 9.5,17.5A1.5,1.5 0 0,1 11,16Z" />
-</vector>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/cookies_ghosted_night.xml b/app/src/main/res/drawable/cookies_ghosted_night.xml
deleted file mode 100644 (file)
index c2c2994..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<!-- This file was created by Google and downloaded from <https://materialdesignicons.com/icon/cookie>.  It is released under the Apache License 2.0. -->
-
-<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true"` not applying to API < 21. -->
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:height="26dp"
-    android:width="26dp"
-    android:viewportHeight="24"
-    android:viewportWidth="24"
-    android:autoMirrored="true"
-    tools:ignore="VectorRaster" >
-
-    <!-- A hard coded color must be used until API >= 21.  Then `@color` or `?attr/colorControlNormal` may be used instead. -->
-    <path
-        android:fillColor="#FF616161"
-        android:pathData="M12,3A9,9 0 0,0 3,12A9,9 0 0,0 12,21A9,9 0 0,0 21,12C21,11.5 20.96,11 20.87,10.5C20.6,10 20,10 20,10H18V9C18,8 17,8 17,8H15V7C15,6 14,6 14,6H13V4C13,3 12,3 12,3M9.5,6A1.5,1.5 0 0,1 11,7.5A1.5,1.5 0 0,1 9.5,9A1.5,1.5 0 0,1 8,7.5A1.5,1.5 0 0,1 9.5,6M6.5,10A1.5,1.5 0 0,1 8,11.5A1.5,1.5 0 0,1 6.5,13A1.5,1.5 0 0,1 5,11.5A1.5,1.5 0 0,1 6.5,10M11.5,11A1.5,1.5 0 0,1 13,12.5A1.5,1.5 0 0,1 11.5,14A1.5,1.5 0 0,1 10,12.5A1.5,1.5 0 0,1 11.5,11M16.5,13A1.5,1.5 0 0,1 18,14.5A1.5,1.5 0 0,1 16.5,16H16.5A1.5,1.5 0 0,1 15,14.5H15A1.5,1.5 0 0,1 16.5,13M11,16A1.5,1.5 0 0,1 12.5,17.5A1.5,1.5 0 0,1 11,19A1.5,1.5 0 0,1 9.5,17.5A1.5,1.5 0 0,1 11,16Z" />
-</vector>
\ No newline at end of file
index a7c9d7d6c31d2635708a29772ba04fe4dd726c0d..8d46b07e7a9f2f1a379eadc7900c74fcfec0a5a2 100644 (file)
     android:layout_height="wrap_content"
     android:layout_width="match_parent" >
 
-    <!-- Align the edit text and the select file button horizontally. -->
     <LinearLayout
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
-        android:orientation="horizontal"
+        android:orientation="vertical"
         android:layout_marginTop="10dp"
         android:layout_marginStart="10dp"
         android:layout_marginEnd="10dp" >
         <!-- The text input layout makes the `android:hint` float above the edit text. -->
         <com.google.android.material.textfield.TextInputLayout
             android:layout_height="wrap_content"
-            android:layout_width="0dp"
-            android:layout_weight="1" >
+            android:layout_width="match_parent" >
 
-            <!-- `android:inputType="textUri"` disables spell check and places an `/` on the main keyboard. -->
+            <!-- `android:inputType="TextUri"` disables spell check and places an `/` on the main keyboard. -->
             <com.google.android.material.textfield.TextInputEditText
-                android:id="@+id/file_name_edittext"
+                android:id="@+id/url_edittext"
                 android:layout_height="wrap_content"
                 android:layout_width="match_parent"
-                android:hint="@string/file_name"
+                android:hint="@string/url"
                 android:inputType="textMultiLine|textUri" />
         </com.google.android.material.textfield.TextInputLayout>
 
-        <Button
-            android:id="@+id/browse_button"
+        <!-- File size. -->
+        <TextView
+            android:id="@+id/file_size_textview"
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
-            android:layout_gravity="center_vertical"
-            android:text="@string/browse" />
+            android:layout_marginEnd="3dp"
+            android:layout_marginBottom="5dp"
+            android:layout_gravity="end" />
     </LinearLayout>
 </ScrollView>
\ No newline at end of file
diff --git a/app/src/main/res/layout/save_webpage_dialog.xml b/app/src/main/res/layout/save_webpage_dialog.xml
deleted file mode 100644 (file)
index 3ff59a5..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-  Copyright © 2019-2021 Soren Stoutner <soren@stoutner.com>.
-
-  This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
-
-  Privacy Browser is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 3 of the License, or
-  (at your option) any later version.
-
-  Privacy Browser is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>. -->
-
-<ScrollView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_height="wrap_content"
-    android:layout_width="match_parent" >
-
-    <LinearLayout
-        android:layout_height="wrap_content"
-        android:layout_width="match_parent"
-        android:orientation="vertical"
-        android:layout_marginTop="10dp"
-        android:layout_marginStart="10dp"
-        android:layout_marginEnd="10dp" >
-
-        <!-- The text input layout makes the `android:hint` float above the edit text. -->
-        <com.google.android.material.textfield.TextInputLayout
-            android:id="@+id/url_textinputlayout"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent" >
-
-            <!-- `android:inputType="TextUri"` disables spell check and places an `/` on the main keyboard. -->
-            <com.google.android.material.textfield.TextInputEditText
-                android:id="@+id/url_edittext"
-                android:layout_height="wrap_content"
-                android:layout_width="match_parent"
-                android:hint="@string/url"
-                android:inputType="textMultiLine|textUri" />
-        </com.google.android.material.textfield.TextInputLayout>
-
-        <!-- File size. -->
-        <TextView
-            android:id="@+id/file_size_textview"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_marginEnd="3dp"
-            android:layout_marginBottom="5dp"
-            android:layout_gravity="end" />
-
-        <!-- Align the edit text and the select file button horizontally. -->
-        <LinearLayout
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            android:orientation="horizontal" >
-
-            <!-- The text input layout makes the `android:hint` float above the edit text. -->
-            <com.google.android.material.textfield.TextInputLayout
-                android:layout_height="wrap_content"
-                android:layout_width="0dp"
-                android:layout_weight="1" >
-
-                <!-- `android:inputType="textUri"` disables spell check and places an `/` on the main keyboard. -->
-                <com.google.android.material.textfield.TextInputEditText
-                    android:id="@+id/file_name_edittext"
-                    android:layout_height="wrap_content"
-                    android:layout_width="match_parent"
-                    android:hint="@string/file_name"
-                    android:inputType="textMultiLine|textUri" />
-            </com.google.android.material.textfield.TextInputLayout>
-
-            <Button
-                android:id="@+id/browse_button"
-                android:layout_height="wrap_content"
-                android:layout_width="wrap_content"
-                android:layout_gravity="center_vertical"
-                android:text="@string/browse" />
-        </LinearLayout>
-    </LinearLayout>
-</ScrollView>
\ No newline at end of file
index 5d369bf980ffe66c0f72ffbd07e1ccb6cf3e47c0..6560b22f4bf545d0fb3d31a02630180c32748919 100644 (file)
@@ -94,9 +94,7 @@
         <item name="blockAdsBlueIcon">@drawable/block_ads_enabled_night</item>
         <item name="copyBlueIcon">@drawable/copy_enabled_night</item>
         <item name="domainsBlueIcon">@drawable/domains_night</item>
-        <item name="domStorageBlueIcon">@drawable/dom_storage_cleared_night</item>
         <item name="fontSizeBlueIcon">@drawable/font_size_night</item>
-        <item name="imagesBlueIcon">@drawable/images_enabled_night</item>
         <item name="lockBlueIcon">@drawable/lock_night</item>
         <item name="moveToFolderBlueIcon">@drawable/move_to_folder_blue_night</item>
         <item name="proxyBlueIcon">@drawable/proxy_enabled_night</item>
index 860772889db19659ba71ae9a4e107dffe24ddc13..a9140535cd66cd5d4551431ec07b5666c980cfda 100644 (file)
@@ -98,9 +98,7 @@
         <item name="blockAdsBlueIcon">@drawable/block_ads_enabled_night</item>
         <item name="copyBlueIcon">@drawable/copy_enabled_night</item>
         <item name="domainsBlueIcon">@drawable/domains_night</item>
-        <item name="domStorageBlueIcon">@drawable/dom_storage_cleared_night</item>
         <item name="fontSizeBlueIcon">@drawable/font_size_night</item>
-        <item name="imagesBlueIcon">@drawable/images_enabled_night</item>
         <item name="lockBlueIcon">@drawable/lock_night</item>
         <item name="moveToFolderBlueIcon">@drawable/move_to_folder_blue_night</item>
         <item name="proxyBlueIcon">@drawable/proxy_enabled_night</item>
index c9c6bf7aaf36499dd98e495c59c4563e08f6894b..477a095bfee8bee02721fce6d819618277cf807d 100644 (file)
@@ -86,9 +86,7 @@
         <item name="blockAdsBlueIcon">@drawable/block_ads_enabled_night</item>
         <item name="copyBlueIcon">@drawable/copy_enabled_night</item>
         <item name="domainsBlueIcon">@drawable/domains_night</item>
-        <item name="domStorageBlueIcon">@drawable/dom_storage_cleared_night</item>
         <item name="fontSizeBlueIcon">@drawable/font_size_night</item>
-        <item name="imagesBlueIcon">@drawable/images_enabled_night</item>
         <item name="lockBlueIcon">@drawable/lock_night</item>
         <item name="moveToFolderBlueIcon">@drawable/move_to_folder_blue_night</item>
         <item name="proxyBlueIcon">@drawable/proxy_enabled_night</item>
index 135a558d6e4615c708acb4d29333777a5c80cfbd..49bf2aa8a787e001df29664ba957f209a585b375 100644 (file)
@@ -94,9 +94,7 @@
         <item name="blockAdsBlueIcon">@drawable/block_ads_enabled_day</item>
         <item name="copyBlueIcon">@drawable/copy_enabled_day</item>
         <item name="domainsBlueIcon">@drawable/domains_day</item>
-        <item name="domStorageBlueIcon">@drawable/dom_storage_cleared_day</item>
         <item name="fontSizeBlueIcon">@drawable/font_size_day</item>
-        <item name="imagesBlueIcon">@drawable/images_enabled_day</item>
         <item name="lockBlueIcon">@drawable/lock_day</item>
         <item name="moveToFolderBlueIcon">@drawable/move_to_folder_blue_day</item>
         <item name="proxyBlueIcon">@drawable/proxy_enabled_day</item>
index d8320f9b3115b8515a0e77f16f606bbf88ac4dc5..38e2881f53b785573f4792cd84023758116b2f29 100644 (file)
@@ -98,9 +98,7 @@
         <item name="blockAdsBlueIcon">@drawable/block_ads_enabled_day</item>
         <item name="copyBlueIcon">@drawable/copy_enabled_day</item>
         <item name="domainsBlueIcon">@drawable/domains_day</item>
-        <item name="domStorageBlueIcon">@drawable/dom_storage_cleared_day</item>
         <item name="fontSizeBlueIcon">@drawable/font_size_day</item>
-        <item name="imagesBlueIcon">@drawable/images_enabled_day</item>
         <item name="lockBlueIcon">@drawable/lock_day</item>
         <item name="moveToFolderBlueIcon">@drawable/move_to_folder_blue_day</item>
         <item name="proxyBlueIcon">@drawable/proxy_enabled_day</item>
index ca8f69247df81748802eed9c1f45e789e7bfc67e..20158b3d46a9f4d458cd4c77aa6a6e224d817fe1 100644 (file)
@@ -67,9 +67,7 @@
     <attr name="blockAdsBlueIcon" format="reference" />
     <attr name="copyBlueIcon" format="reference" />
     <attr name="domainsBlueIcon" format="reference" />
-    <attr name="domStorageBlueIcon" format="reference" />
     <attr name="fontSizeBlueIcon" format="reference" />
-    <attr name="imagesBlueIcon" format="reference" />
     <attr name="lockBlueIcon" format="reference" />
     <attr name="moveToFolderBlueIcon" format="reference" />
     <attr name="proxyBlueIcon" format="reference" />
index 411cbede6b7f4f2a95c147312d0f5191e768482e..f173412e43fce343e43ee6759be358aeff2adcf9 100644 (file)
@@ -86,9 +86,7 @@
         <item name="blockAdsBlueIcon">@drawable/block_ads_enabled_day</item>
         <item name="copyBlueIcon">@drawable/copy_enabled_day</item>
         <item name="domainsBlueIcon">@drawable/domains_day</item>
-        <item name="domStorageBlueIcon">@drawable/dom_storage_cleared_day</item>
         <item name="fontSizeBlueIcon">@drawable/font_size_day</item>
-        <item name="imagesBlueIcon">@drawable/images_enabled_day</item>
         <item name="lockBlueIcon">@drawable/lock_day</item>
         <item name="moveToFolderBlueIcon">@drawable/move_to_folder_blue_day</item>
         <item name="proxyBlueIcon">@drawable/proxy_enabled_day</item>