Add a warning if a file will be overwritten. https://redmine.stoutner.com/issues/371
authorSoren Stoutner <soren@stoutner.com>
Wed, 15 Jan 2020 21:30:56 +0000 (14:30 -0700)
committerSoren Stoutner <soren@stoutner.com>
Wed, 15 Jan 2020 21:30:56 +0000 (14:30 -0700)
app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveLogcatDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveWebpageDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java
app/src/main/res/layout/import_export_coordinatorlayout.xml
app/src/main/res/layout/open_dialog.xml
app/src/main/res/layout/save_dialog.xml
app/src/main/res/values/strings.xml

index ca4703af5243f936774f8ffd68cf2ec7e57a7ab1..df41cf31f6502f5aa86f506e3be11ab3dbac9285 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2018-2019 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2018-2020 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
  *
@@ -151,6 +151,8 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe
         RadioButton exportRadioButton = findViewById(R.id.export_radiobutton);
         LinearLayout fileNameLinearLayout = findViewById(R.id.file_name_linearlayout);
         EditText fileNameEditText = findViewById(R.id.file_name_edittext);
+        TextView fileDoesNotExistTextView = findViewById(R.id.file_does_not_exist_textview);
+        TextView fileExistsWarningTextView = findViewById(R.id.file_exists_warning_textview);
         TextView openKeychainImportInstructionsTextView = findViewById(R.id.openkeychain_import_instructions_textview);
         Button importExportButton = findViewById(R.id.import_export_button);
         TextView storagePermissionTextView = findViewById(R.id.import_export_storage_permission_textview);
@@ -169,28 +171,36 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe
         kitKatPasswordEncryptionTextView.setVisibility(View.GONE);
         openKeychainRequiredTextView.setVisibility(View.GONE);
         fileNameLinearLayout.setVisibility(View.GONE);
+        fileDoesNotExistTextView.setVisibility(View.GONE);
+        fileExistsWarningTextView.setVisibility(View.GONE);
         openKeychainImportInstructionsTextView.setVisibility(View.GONE);
         importExportButton.setVisibility(View.GONE);
 
         // Create strings for the default file paths.
         String defaultFilePath;
         String defaultPasswordEncryptionFilePath;
+        String defaultPgpFilePath;
 
         // Set the default file paths according to the storage permission status.
         if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {  // The storage permission has been granted.
             // Set the default file paths to use the external public directory.
             defaultFilePath = Environment.getExternalStorageDirectory() + "/" + getString(R.string.settings_pbs);
             defaultPasswordEncryptionFilePath = defaultFilePath + ".aes";
+            defaultPgpFilePath = defaultFilePath + ".pgp";
+
+            // Hide the storage permission text view.
+            storagePermissionTextView.setVisibility(View.GONE);
         } else {  // The storage permission has not been granted.
             // Set the default file paths to use the external private directory.
             defaultFilePath = getApplicationContext().getExternalFilesDir(null) + "/" + getString(R.string.settings_pbs);
             defaultPasswordEncryptionFilePath = defaultFilePath + ".aes";
+            defaultPgpFilePath = defaultFilePath + ".pgp";
         }
 
         // Set the default file path.
         fileNameEditText.setText(defaultFilePath);
 
-        // Display the encryption information when the spinner changes.
+        // Update the UI when the spinner changes.
         encryptionSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
             @Override
             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
@@ -217,9 +227,6 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe
 
                         // Reset the default file path.
                         fileNameEditText.setText(defaultFilePath);
-
-                        // Enable the import/export button if a file name exists.
-                        importExportButton.setEnabled(!fileNameEditText.getText().toString().isEmpty());
                         break;
 
                     case PASSWORD_ENCRYPTION:
@@ -253,9 +260,6 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe
 
                             // Update the default file path.
                             fileNameEditText.setText(defaultPasswordEncryptionFilePath);
-
-                            // Enable the import/export button if a password exists.
-                            importExportButton.setEnabled(!encryptionPasswordEditText.getText().toString().isEmpty());
                         }
                         break;
 
@@ -266,8 +270,8 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe
 
                         // Updated items based on the installation status of OpenKeychain.
                         if (openKeychainInstalled) {  // OpenKeychain is installed.
-                            // Remove the default file path.
-                            fileNameEditText.setText("");
+                            // Update the default file path.
+                            fileNameEditText.setText(defaultPgpFilePath);
 
                             // Show the file location card.
                             fileLocationCardView.setVisibility(View.VISIBLE);
@@ -279,16 +283,10 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe
 
                                 // Set the text of the import button to be `Decrypt`.
                                 importExportButton.setText(R.string.decrypt);
-
-                                // Disable the import/export button.  The user needs to select a file to import first.
-                                importExportButton.setEnabled(false);
                             } else if (exportRadioButton.isChecked()) {
                                 // Hide the file name linear layout and the OpenKeychain import instructions.
                                 fileNameLinearLayout.setVisibility(View.GONE);
                                 openKeychainImportInstructionsTextView.setVisibility(View.GONE);
-
-                                // Enable the import/export button.
-                                importExportButton.setEnabled(true);
                             }
                         } else {  // OpenKeychain is not installed.
                             // Show the OpenPGP required layout item.
@@ -321,12 +319,24 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe
 
             @Override
             public void afterTextChanged(Editable s) {
-                // Enable the import/export button if a file name and password exists.
-                importExportButton.setEnabled(!fileNameEditText.getText().toString().isEmpty() && !encryptionPasswordEditText.getText().toString().isEmpty());
+                // Get the current file name.
+                String fileNameString = fileNameEditText.getText().toString();
+
+                // Convert the file name string to a file.
+                File file = new File(fileNameString);
+
+                // Update the import/export button.
+                if (importRadioButton.isChecked()) {  // The import radio button is checked.
+                    // Enable the import button if the file and the password exists.
+                    importExportButton.setEnabled(file.exists() && !encryptionPasswordEditText.getText().toString().isEmpty());
+                } else if (exportRadioButton.isChecked()) {  // The export radio button is checked.
+                    // Enable the export button if the file string and the password exists.
+                    importExportButton.setEnabled(!fileNameString.isEmpty() && !encryptionPasswordEditText.getText().toString().isEmpty());
+                }
             }
         });
 
-        // Update the status of the import/export button when the file name EditText changes.
+        // Update the UI when the file name EditText changes.
         fileNameEditText.addTextChangedListener(new TextWatcher() {
             @Override
             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -340,40 +350,145 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe
 
             @Override
             public void afterTextChanged(Editable s) {
-                // Adjust the export button according to the encryption spinner position.
+                // Get the current file name.
+                String fileNameString = fileNameEditText.getText().toString();
+
+                // Convert the file name string to a file.
+                File file = new File(fileNameString);
+
+                // Adjust the UI according to the encryption spinner position.
                 switch (encryptionSpinner.getSelectedItemPosition()) {
                     case NO_ENCRYPTION:
-                        // Enable the import/export button if a file name exists.
-                        importExportButton.setEnabled(!fileNameEditText.getText().toString().isEmpty());
+                        // Determine if import or export is checked.
+                        if (exportRadioButton.isChecked()) {  // The export radio button is checked.
+                            // Hide the file does not exist text view.
+                            fileDoesNotExistTextView.setVisibility(View.GONE);
+
+                            // Display a warning if the file already exists.
+                            if (file.exists()) {
+                                fileExistsWarningTextView.setVisibility(View.VISIBLE);
+                            } else {
+                                fileExistsWarningTextView.setVisibility(View.GONE);
+                            }
+
+                            // Enable the export button if the file name is populated.
+                            importExportButton.setEnabled(!fileNameString.isEmpty());
+                        } else if (importRadioButton.isChecked()) {  // The import radio button is checked.
+                            // Hide the file exists warning text view.
+                            fileExistsWarningTextView.setVisibility(View.GONE);
+
+                            // Check if the file exists.
+                            if (file.exists()) {  // The file exists.
+                                // Hide the notification that the file does not exist.
+                                fileDoesNotExistTextView.setVisibility(View.GONE);
+
+                                // Enable the import button.
+                                importExportButton.setEnabled(true);
+                            } else {  // The file does not exist.
+                                // Show a notification that the file does not exist.
+                                fileDoesNotExistTextView.setVisibility(View.VISIBLE);
+
+                                // Disable the import button.
+                                importExportButton.setEnabled(false);
+                            }
+                        } else {  // Neither radio button is checked.
+                            // Hide the file notification text views.
+                            fileExistsWarningTextView.setVisibility(View.GONE);
+                            fileDoesNotExistTextView.setVisibility(View.GONE);
+                        }
                         break;
 
                     case PASSWORD_ENCRYPTION:
-                        // Enable the import/export button if a file name and password exists.
-                        importExportButton.setEnabled(!fileNameEditText.getText().toString().isEmpty() && !encryptionPasswordEditText.getText().toString().isEmpty());
+                        // Determine if import or export is checked.
+                        if (exportRadioButton.isChecked()) {  // The export radio button is checked.
+                            // Hide the notification that the file does not exist.
+                            fileDoesNotExistTextView.setVisibility(View.GONE);
+
+                            // Display a warning if the file already exists.
+                            if (file.exists()) {
+                                fileExistsWarningTextView.setVisibility(View.VISIBLE);
+                            } else {
+                                fileExistsWarningTextView.setVisibility(View.GONE);
+                            }
+
+                            // Enable the export button if the file name and the password are populated.
+                            importExportButton.setEnabled(!fileNameString.isEmpty() && !encryptionPasswordEditText.getText().toString().isEmpty());
+                        } else if (importRadioButton.isChecked()) {  // The import radio button is checked.
+                            // Hide the file exists warning text view.
+                            fileExistsWarningTextView.setVisibility(View.GONE);
+
+                            // Check if the file exists.
+                            if (file.exists()) {  // The file exists.
+                                // Hide the notification that the file does not exist.
+                                fileDoesNotExistTextView.setVisibility(View.GONE);
+
+                                // Enable the import button if the password is populated.
+                                importExportButton.setEnabled(!encryptionPasswordEditText.getText().toString().isEmpty());
+                            } else {  // The file does not exist.
+                                // Show a notification that the file does not exist.
+                                fileDoesNotExistTextView.setVisibility(View.VISIBLE);
+
+                                // Disable the import button.
+                                importExportButton.setEnabled(false);
+                            }
+                        } else {  // Neither radio button is checked.
+                            // Hide the file notification text views.
+                            fileExistsWarningTextView.setVisibility(View.GONE);
+                            fileDoesNotExistTextView.setVisibility(View.GONE);
+                        }
                         break;
 
                     case OPENPGP_ENCRYPTION:
-                        // Enable the import/export button if OpenKeychain is installed and a file name exists.
-                        importExportButton.setEnabled(openKeychainInstalled && !fileNameEditText.getText().toString().isEmpty());
+                        // Hide the file exists warning text view.
+                        fileExistsWarningTextView.setVisibility(View.GONE);
+
+                        if (importRadioButton.isChecked()) {  // The import radio button is checked.
+                            if (file.exists()) {  // The file exists.
+                                // Hide the notification that the file does not exist.
+                                fileDoesNotExistTextView.setVisibility(View.GONE);
+
+                                // Enable the import button if OpenKeychain is installed.
+                                importExportButton.setEnabled(openKeychainInstalled);
+                            } else {  // The file does not exist.
+                                // Show the notification that the file does not exist.
+                                fileDoesNotExistTextView.setVisibility(View.VISIBLE);
+
+                                // Disable the import button.
+                                importExportButton.setEnabled(false);
+                            }
+                        } else if (exportRadioButton.isChecked()){  // The export radio button is checked.
+                            // Hide the notification that the file does not exist.
+                            fileDoesNotExistTextView.setVisibility(View.GONE);
+
+                            // Enable the export button.
+                            importExportButton.setEnabled(true);
+                        } else {  // Neither radio button is checked.
+                            // Hide the notification that the file does not exist.
+                            fileDoesNotExistTextView.setVisibility(View.GONE);
+                        }
                         break;
                 }
             }
         });
-
-        // Hide the storage permissions text view on API < 23 as permissions on older devices are automatically granted.
-        if (Build.VERSION.SDK_INT < 23) {
-            storagePermissionTextView.setVisibility(View.GONE);
-        }
     }
 
     public void onClickRadioButton(View view) {
         // Get handles for the views.
         Spinner encryptionSpinner = findViewById(R.id.encryption_spinner);
         LinearLayout fileNameLinearLayout = findViewById(R.id.file_name_linearlayout);
+        EditText passwordEncryptionEditText = findViewById(R.id.password_encryption_edittext);
         EditText fileNameEditText = findViewById(R.id.file_name_edittext);
+        TextView fileDoesNotExistTextView = findViewById(R.id.file_does_not_exist_textview);
+        TextView fileExistsWarningTextView = findViewById(R.id.file_exists_warning_textview);
         TextView openKeychainImportInstructionTextView = findViewById(R.id.openkeychain_import_instructions_textview);
         Button importExportButton = findViewById(R.id.import_export_button);
 
+        // Get the current file name.
+        String fileNameString = fileNameEditText.getText().toString();
+
+        // Convert the file name string to a file.
+        File file = new File(fileNameString);
+
         // Check to see if import or export was selected.
         switch (view.getId()) {
             case R.id.import_radiobutton:
@@ -384,9 +499,6 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe
 
                     // Set the text on the import/export button to be `Decrypt`.
                     importExportButton.setText(R.string.decrypt);
-
-                    // Enable the decrypt button if there is a file name.
-                    importExportButton.setEnabled(!fileNameEditText.getText().toString().isEmpty());
                 } else {  // OpenPGP encryption not selected.
                     // Hide the OpenKeychain import instructions.
                     openKeychainImportInstructionTextView.setVisibility(View.GONE);
@@ -395,9 +507,33 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe
                     importExportButton.setText(R.string.import_button);
                 }
 
+                // Hide the file exists warning text view.
+                fileExistsWarningTextView.setVisibility(View.GONE);
+
                 // Display the file name views.
                 fileNameLinearLayout.setVisibility(View.VISIBLE);
                 importExportButton.setVisibility(View.VISIBLE);
+
+                // Check to see if the file exists.
+                if (file.exists()) {  // The file exists.
+                    // Hide the notification that the file does not exist.
+                    fileDoesNotExistTextView.setVisibility(View.GONE);
+
+                    // Check to see if password encryption is selected.
+                    if (encryptionSpinner.getSelectedItemPosition() == PASSWORD_ENCRYPTION) {  // Password encryption is selected.
+                        // Enable the import button if the encryption password is populated.
+                        importExportButton.setEnabled(!passwordEncryptionEditText.getText().toString().isEmpty());
+                    } else {  // Password encryption is not selected.
+                        // Enable the import/decrypt button.
+                        importExportButton.setEnabled(true);
+                    }
+                } else {  // The file does not exist.
+                    // Show the notification that the file does not exist.
+                    fileDoesNotExistTextView.setVisibility(View.VISIBLE);
+
+                    // Disable the import/decrypt button.
+                    importExportButton.setEnabled(false);
+                }
                 break;
 
             case R.id.export_radiobutton:
@@ -414,19 +550,41 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe
                 if (encryptionSpinner.getSelectedItemPosition() == OPENPGP_ENCRYPTION) {  // OpenPGP encryption is selected.
                     // Hide the file name views.
                     fileNameLinearLayout.setVisibility(View.GONE);
+                    fileDoesNotExistTextView.setVisibility(View.GONE);
+                    fileExistsWarningTextView.setVisibility(View.GONE);
 
                     // Enable the export button.
                     importExportButton.setEnabled(true);
                 } else {  // OpenPGP encryption is not selected.
-                    // Show the file name views.
+                    // Show the file name view.
                     fileNameLinearLayout.setVisibility(View.VISIBLE);
+
+                    // Hide the notification that the file name does not exist.
+                    fileDoesNotExistTextView.setVisibility(View.GONE);
+
+                    // Display a warning if the file already exists.
+                    if (file.exists()) {
+                        fileExistsWarningTextView.setVisibility(View.VISIBLE);
+                    } else {
+                        fileExistsWarningTextView.setVisibility(View.GONE);
+                    }
+
+                    // Check the encryption type.
+                    if (encryptionSpinner.getSelectedItemPosition() == NO_ENCRYPTION) {  // No encryption is selected.
+                        // Enable the export button if the file name is populated.
+                        importExportButton.setEnabled(!fileNameString.isEmpty());
+                    } else {  // Password encryption is selected.
+                        // Enable the export button if the file name and the password are populated.
+                        importExportButton.setEnabled(!fileNameString.isEmpty() && !passwordEncryptionEditText.getText().toString().isEmpty());
+                    }
                 }
                 break;
         }
     }
 
     public void browse(View view) {
-        // Get a handle for the import radiobutton.
+        // Get a handle for the views.
+        Spinner encryptionSpinner = findViewById(R.id.encryption_spinner);
         RadioButton importRadioButton = findViewById(R.id.import_radiobutton);
 
         // Check to see if import or export is selected.
@@ -454,8 +612,12 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe
             // Set the intent MIME type to include all files so that everything is visible.
             exportBrowseIntent.setType("*/*");
 
-            // Set the initial export file name.
-            exportBrowseIntent.putExtra(Intent.EXTRA_TITLE, getString(R.string.settings_pbs));
+            // Set the initial export file name according to the encryption type.
+            if (encryptionSpinner.getSelectedItemPosition() == NO_ENCRYPTION) {  // No encryption is selected.
+                exportBrowseIntent.putExtra(Intent.EXTRA_TITLE, getString(R.string.settings_pbs));
+            } else {  // Password encryption is selected.
+                exportBrowseIntent.putExtra(Intent.EXTRA_TITLE, getString(R.string.settings_pbs) + ".aes");
+            }
 
             // Set the initial directory if the minimum API >= 26.
             if (Build.VERSION.SDK_INT >= 26) {
@@ -567,8 +729,9 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe
             case (BROWSE_RESULT_CODE):
                 // Don't do anything if the user pressed back from the file picker.
                 if (resultCode == Activity.RESULT_OK) {
-                    // Get a handle for the file name edit text.
+                    // Get a handle for the views.
                     EditText fileNameEditText = findViewById(R.id.file_name_edittext);
+                    TextView fileExistsWarningTextView = findViewById(R.id.file_exists_warning_textview);
 
                     // Instantiate the file name helper.
                     FileNameHelper fileNameHelper = new FileNameHelper();
@@ -583,6 +746,9 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe
 
                         // Set the file name path as the text of the file name edit text.
                         fileNameEditText.setText(fileNamePath);
+
+                        // Hide the file exists warning text view, because the file picker will have just created a file if export was selected.
+                        fileExistsWarningTextView.setVisibility(View.GONE);
                     }
                 }
                 break;
index c38181a60239d81e190b1274ba8e94422ac73de8..9f433632419b622ba129178ded8f3f2429c7ca44 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2019 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2019-2020 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
  *
@@ -34,6 +34,7 @@ import android.os.Bundle;
 import android.preference.PreferenceManager;
 import android.view.Menu;
 import android.view.MenuItem;
+import android.view.View;
 import android.view.WindowManager;
 import android.widget.EditText;
 import android.widget.TextView;
@@ -41,7 +42,7 @@ import android.widget.TextView;
 import androidx.annotation.NonNull;
 import androidx.appcompat.app.ActionBar;
 import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;  // The AndroidX toolbar must be used until the minimum API is >= 21.
+import androidx.appcompat.widget.Toolbar;
 import androidx.core.app.ActivityCompat;
 import androidx.core.content.ContextCompat;
 import androidx.fragment.app.DialogFragment;
@@ -335,8 +336,9 @@ public class LogcatActivity extends AppCompatActivity implements SaveLogcatDialo
                 // Remove the lint warning below that the save dialog might be null.
                 assert saveDialog != null;
 
-                // Get a handle for the file name edit text.
+                // Get a handle for the dialog views.
                 EditText fileNameEditText = saveDialog.findViewById(R.id.file_name_edittext);
+                TextView fileExistsWarningTextView = saveDialog.findViewById(R.id.file_exists_warning_textview);
 
                 // Instantiate the file name helper.
                 FileNameHelper fileNameHelper = new FileNameHelper();
@@ -351,6 +353,12 @@ public class LogcatActivity extends AppCompatActivity implements SaveLogcatDialo
 
                     // Set the file name path as the text of the file name edit text.
                     fileNameEditText.setText(fileNamePath);
+
+                    // Move the cursor to the end of the file name edit text.
+                    fileNameEditText.setSelection(fileNamePath.length());
+
+                    // Hide the file exists warning.
+                    fileExistsWarningTextView.setVisibility(View.GONE);
                 }
             }
         }
index 1904673b5ff6543a0617772e62d03cc9ac828eba..4cef681385533e224a1d58f4aba192f63d784a45 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2015-2019 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2015-2020 Soren Stoutner <soren@stoutner.com>.
  *
  * Download cookie code contributed 2017 Hendrik Knackstedt.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
  *
@@ -2940,6 +2940,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                         // Get a handle for the file name edit text.
                         EditText fileNameEditText = saveWebpageDialog.findViewById(R.id.file_name_edittext);
+                        TextView fileExistsWarningTextView = saveWebpageDialog.findViewById(R.id.file_exists_warning_textview);
 
                         // Instantiate the file name helper.
                         FileNameHelper fileNameHelper = new FileNameHelper();
@@ -2954,6 +2955,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                             // Move the cursor to the end of the file name edit text.
                             fileNameEditText.setSelection(fileNamePath.length());
+
+                            // Hide the file exists warning.
+                            fileExistsWarningTextView.setVisibility(View.GONE);
                         }
                     }
                 }
index b478d726f69d83da1c64475dc5455f054bbb97dc..2d6a776153b3dc746e37a02a9703175d6530c4f5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2019 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2019-2020 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
  *
@@ -41,14 +41,16 @@ import android.widget.Button;
 import android.widget.EditText;
 import android.widget.TextView;
 
-import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.activities.MainWebViewActivity;
-
 import androidx.annotation.NonNull;
 import androidx.core.content.ContextCompat;
 import androidx.fragment.app.DialogFragment;
 import androidx.preference.PreferenceManager;
 
+import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.activities.MainWebViewActivity;
+
+import java.io.File;
+
 public class OpenDialog extends DialogFragment {
     // Define the open listener.
     private OpenListener openListener;
@@ -137,27 +139,13 @@ public class OpenDialog extends DialogFragment {
         // Get handles for the layout items.
         EditText fileNameEditText = alertDialog.findViewById(R.id.file_name_edittext);
         Button browseButton = alertDialog.findViewById(R.id.browse_button);
+        TextView fileDoesNotExistTextView = alertDialog.findViewById(R.id.file_does_not_exist_textview);
         TextView storagePermissionTextView = alertDialog.findViewById(R.id.storage_permission_textview);
         Button openButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
 
         // Create a string for the default file path.
         String defaultFilePath;
 
-        // Set the default file path according to the storage permission state.
-        if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {  // The storage permission has been granted.
-            // Set the default file path to use the external public directory.
-            defaultFilePath = Environment.getExternalStorageDirectory() + "/";
-        } else {  // The storage permission has not been granted.
-            // Set the default file path to use the external private directory.
-            defaultFilePath = context.getExternalFilesDir(null) + "/";
-        }
-
-        // Display the default file path.
-        fileNameEditText.setText(defaultFilePath);
-
-        // Move the cursor to the end of the default file path.
-        fileNameEditText.setSelection(defaultFilePath.length());
-
         // Update the status of the open button when the file name changes.
         fileNameEditText.addTextChangedListener(new TextWatcher() {
             @Override
@@ -172,11 +160,47 @@ public class OpenDialog extends DialogFragment {
 
             @Override
             public void afterTextChanged(Editable editable) {
-                // Enable the open button if a file name exists.
-                openButton.setEnabled(!fileNameEditText.getText().toString().isEmpty());
+                // Get the current file name.
+                String fileNameString = fileNameEditText.getText().toString();
+
+                // Convert the file name string to a file.
+                File file = new File(fileNameString);
+
+                // Check to see if the file exists.
+                if (file.exists()) {  // The file exists.
+                    // Hide the notification that the file does not exist.
+                    fileDoesNotExistTextView.setVisibility(View.GONE);
+
+                    // Enable the open button.
+                    openButton.setEnabled(true);
+                } else {  // The file does not exist.
+                    // Show the notification that the file does not exist.
+                    fileDoesNotExistTextView.setVisibility(View.VISIBLE);
+
+                    // Disable the open button.
+                    openButton.setEnabled(false);
+                }
             }
         });
 
+        // Set the default file path according to the storage permission state.
+        if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {  // The storage permission has been granted.
+            // Set the default file path to use the external public directory.
+            defaultFilePath = Environment.getExternalStorageDirectory() + "/";
+
+            // Hide the storage permission text view.
+            storagePermissionTextView.setVisibility(View.GONE);
+        } else {  // The storage permission has not been granted.
+            // Set the default file path to use the external private directory.
+            defaultFilePath = context.getExternalFilesDir(null) + "/";
+        }
+
+        // Display the default file path.
+        fileNameEditText.setText(defaultFilePath);
+
+        // Move the cursor to the end of the default file path.
+        fileNameEditText.setSelection(defaultFilePath.length());
+
         // Handle clicks on the browse button.
         browseButton.setOnClickListener((View view) -> {
             // Create the file picker intent.
@@ -194,11 +218,6 @@ public class OpenDialog extends DialogFragment {
             activity.startActivityForResult(browseIntent, MainWebViewActivity.BROWSE_OPEN_REQUEST_CODE);
         });
 
-        // Hide the storage permission text view on API < 23 as permissions on older devices are automatically granted.
-        if (Build.VERSION.SDK_INT < 23 || (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) {
-            storagePermissionTextView.setVisibility(View.GONE);
-        }
-
         // Return the alert dialog.
         return alertDialog;
     }
index b49958ebe7a8f898d1973fc747b3167c20e4c08d..9e27f529a980e90a32c3e6a114cc53b039afda04 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
  *
@@ -48,6 +48,8 @@ import androidx.fragment.app.DialogFragment;  // The AndroidX dialog fragment is
 
 import com.stoutner.privacybrowser.R;
 
+import java.io.File;
+
 public class SaveLogcatDialog extends DialogFragment {
     // Define the save logcat listener.
     private SaveLogcatListener saveLogcatListener;
@@ -135,9 +137,44 @@ public class SaveLogcatDialog extends DialogFragment {
         // Get handles for the layout items.
         EditText fileNameEditText = alertDialog.findViewById(R.id.file_name_edittext);
         Button browseButton = alertDialog.findViewById(R.id.browse_button);
+        TextView fileExistsWarningTextView = alertDialog.findViewById(R.id.file_exists_warning_textview);
         TextView storagePermissionTextView = alertDialog.findViewById(R.id.storage_permission_textview);
         Button saveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
 
+        // Update the status of the save button when the file name changes.
+        fileNameEditText.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+                // Do nothing.
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+                // Do nothing.
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                // Get the current file name.
+                String fileNameString = fileNameEditText.getText().toString();
+
+                // Convert the file name string to a file.
+                File file = new File(fileNameString);
+
+                // Check to see if the file exists.
+                if (file.exists()) {
+                    // Show the file exists warning.
+                    fileExistsWarningTextView.setVisibility(View.VISIBLE);
+                } else {
+                    // Hide the file exists warning.
+                    fileExistsWarningTextView.setVisibility(View.GONE);
+                }
+
+                // Enable the save button if the file name is populated.
+                saveButton.setEnabled(!fileNameString.isEmpty());
+            }
+        });
+
         // Create a string for the default file path.
         String defaultFilePath;
 
@@ -151,6 +188,9 @@ public class SaveLogcatDialog extends DialogFragment {
         if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {  // The storage permission has been granted.
             // Set the default file path to use the external public directory.
             defaultFilePath = Environment.getExternalStorageDirectory() + "/" + getString(R.string.privacy_browser_logcat_txt);
+
+            // Hide the storage permission text view.
+            storagePermissionTextView.setVisibility(View.GONE);
         } else {  // The storage permission has not been granted.
             // Set the default file path to use the external private directory.
             defaultFilePath = context.getExternalFilesDir(null) + "/" + getString(R.string.privacy_browser_logcat_txt);
@@ -159,25 +199,6 @@ public class SaveLogcatDialog extends DialogFragment {
         // Display the default file path.
         fileNameEditText.setText(defaultFilePath);
 
-        // Update the status of the save button when the file name changes.
-        fileNameEditText.addTextChangedListener(new TextWatcher() {
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-                // Do nothing.
-            }
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before, int count) {
-                // Do nothing.
-            }
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                // Enable the save button if a file name exists.
-                saveButton.setEnabled(!fileNameEditText.getText().toString().isEmpty());
-            }
-        });
-
         // Handle clicks on the browse button.
         browseButton.setOnClickListener((View view) -> {
             // Create the file picker intent.
@@ -201,11 +222,6 @@ public class SaveLogcatDialog extends DialogFragment {
             activity.startActivityForResult(browseIntent, 0);
         });
 
-        // Hide the storage permission text view on API < 23 as permissions on older devices are automatically granted.
-        if (Build.VERSION.SDK_INT < 23) {
-            storagePermissionTextView.setVisibility(View.GONE);
-        }
-
         // Return the alert dialog.
         return alertDialog;
     }
index 72d0f20a6725cf7d65c7ca584243662f470fa4dc..7e4250a06958327b13d34b5a7de4e9a20a9a6e80 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2019 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2019-2020 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
  *
@@ -49,6 +49,8 @@ import androidx.preference.PreferenceManager;
 import com.stoutner.privacybrowser.R;
 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
 
+import java.io.File;
+
 public class SaveWebpageDialog extends DialogFragment {
     // Define the save webpage listener.
     private SaveWebpageListener saveWebpageListener;
@@ -187,9 +189,44 @@ public class SaveWebpageDialog extends DialogFragment {
         // Get handles for the layout items.
         EditText fileNameEditText = alertDialog.findViewById(R.id.file_name_edittext);
         Button browseButton = alertDialog.findViewById(R.id.browse_button);
+        TextView fileExistsWarningTextView = alertDialog.findViewById(R.id.file_exists_warning_textview);
         TextView storagePermissionTextView = alertDialog.findViewById(R.id.storage_permission_textview);
         Button saveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
 
+        // Update the status of the save button when the file name changes.
+        fileNameEditText.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+                // Do nothing.
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+                // Do nothing.
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                // Get the current file name.
+                String fileNameString = fileNameEditText.getText().toString();
+
+                // Convert the file name string to a file.
+                File file = new File(fileNameString);
+
+                // Check to see if the file exists.
+                if (file.exists()) {
+                    // Show the file exists warning.
+                    fileExistsWarningTextView.setVisibility(View.VISIBLE);
+                } else {
+                    // Hide the file exists warning.
+                    fileExistsWarningTextView.setVisibility(View.GONE);
+                }
+
+                // Enable the save button if the file name is populated.
+                saveButton.setEnabled(!fileNameString.isEmpty());
+            }
+        });
+
         // Create a default file name string.
         String defaultFileName = "";
 
@@ -211,6 +248,9 @@ public class SaveWebpageDialog extends DialogFragment {
         if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {  // The storage permission has been granted.
             // Set the default file path to use the external public directory.
             defaultFilePath = Environment.getExternalStorageDirectory() + "/" + defaultFileName;
+
+            // Hide the storage permission text view.
+            storagePermissionTextView.setVisibility(View.GONE);
         } else {  // The storage permission has not been granted.
             // Set the default file path to use the external private directory.
             defaultFilePath = context.getExternalFilesDir(null) + "/" + defaultFileName;
@@ -222,25 +262,6 @@ public class SaveWebpageDialog extends DialogFragment {
         // Move the cursor to the end of the default file path.
         fileNameEditText.setSelection(defaultFilePath.length());
 
-        // Update the status of the save button when the file name changes.
-        fileNameEditText.addTextChangedListener(new TextWatcher() {
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-                // Do nothing.
-            }
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before, int count) {
-                // Do nothing.
-            }
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                // Enable the save button if a file name exists.
-                saveButton.setEnabled(!fileNameEditText.getText().toString().isEmpty());
-            }
-        });
-
         // Handle clicks on the browse button.
         browseButton.setOnClickListener((View view) -> {
             // Create the file picker intent.
@@ -272,11 +293,6 @@ public class SaveWebpageDialog extends DialogFragment {
             activity.startActivityForResult(browseIntent, MainWebViewActivity.BROWSE_SAVE_WEBPAGE_REQUEST_CODE);
         });
 
-        // Hide the storage permission text view on API < 23 as permissions on older devices are automatically granted.
-        if (Build.VERSION.SDK_INT < 23 || (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) {
-            storagePermissionTextView.setVisibility(View.GONE);
-        }
-
         // Return the alert dialog.
         return alertDialog;
     }
index d687546d9581dc32ba8b135a70b70daed38a6d65..b7f0b487246e05bb601efad0346c71eb9381de44 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
  *
@@ -158,7 +158,6 @@ public class ViewSslCertificateDialog extends DialogFragment {
 
             // `onCreateDialog` requires the return of an `AlertDialog`.
             return alertDialog;
-
         } else {  // Display the SSL certificate information
             // Set the title.
             dialogBuilder.setTitle(R.string.ssl_certificate);
index 29b6fe89fccfe68227951737b7d456856feaaffe..b6eebd1fc44a3db4622bc7c5806a8a8fd4d695ff 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright © 2018-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2018-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
                                 android:onClick="browse" />
                         </LinearLayout>
 
+                        <!-- File notices. -->
+                        <TextView
+                            android:id="@+id/file_does_not_exist_textview"
+                            android:layout_height="wrap_content"
+                            android:layout_width="wrap_content"
+                            android:layout_gravity="center_horizontal"
+                            android:layout_margin="5dp"
+                            android:text="@string/file_does_not_exist"
+                            android:textColor="?attr/redText"
+                            android:textAlignment="center" />
+
+                        <TextView
+                            android:id="@+id/file_exists_warning_textview"
+                            android:layout_height="wrap_content"
+                            android:layout_width="wrap_content"
+                            android:layout_gravity="center_horizontal"
+                            android:layout_margin="5dp"
+                            android:text="@string/file_exists_warning"
+                            android:textColor="?attr/redText"
+                            android:textAlignment="center" />
+
                         <!-- OpenKeychain import instructions -->
                         <TextView
                             android:id="@+id/openkeychain_import_instructions_textview"
index dd3059e3ecff57345159033889e62c021f2c5c62..3956b5b8505523b924688d409a67d49e68c50c6c 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright © 2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2019-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
                 android:text="@string/browse" />
         </LinearLayout>
 
+        <!-- File does not exist warning. -->
+        <TextView
+            android:id="@+id/file_does_not_exist_textview"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:layout_margin="5dp"
+            android:text="@string/file_does_not_exist"
+            android:textColor="?attr/redText"
+            android:textAlignment="center" />
+
+        <!-- Storage permission explanation. -->
         <TextView
             android:id="@+id/storage_permission_textview"
             android:layout_height="wrap_content"
index dd3059e3ecff57345159033889e62c021f2c5c62..5615013ba6b91160ccbf96af08c49701d18066b8 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright © 2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2019-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
                 android:text="@string/browse" />
         </LinearLayout>
 
+        <!-- File already exists warning. -->
+        <TextView
+            android:id="@+id/file_exists_warning_textview"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:layout_margin="5dp"
+            android:text="@string/file_exists_warning"
+            android:textColor="?attr/redText"
+            android:textAlignment="center" />
+
+        <!-- Storage permission explanation. -->
         <TextView
             android:id="@+id/storage_permission_textview"
             android:layout_height="wrap_content"
index 22542592d3eb3cb30198991507a023f8a9fe0c55..5e80c4540d2bdd9fd2f33234e612c41ac58a880a 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright © 2015-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2015-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
         <item>OpenPGP</item>
     </string-array>
     <string name="kitkat_password_encryption_message">Password encryption does not work on Android KitKat.</string>
+    <string name="file_does_not_exist">The file does not exist.</string>
+    <string name="file_exists_warning">The file already exists.  If you proceed it will be overwritten.</string>
     <string name="openkeychain_required">OpenPGP encryption requires that OpenKeychain be installed.</string>
     <string name="openkeychain_import_instructions">The unencrypted file will have to be imported in a separate step after it is decrypted.</string>
     <string name="settings_pbs">Settings.pbs</string>