From a87ea266a0df9aca50f9bc8a58066a4c962ea515 Mon Sep 17 00:00:00 2001 From: Soren Stoutner Date: Wed, 15 Jan 2020 14:30:56 -0700 Subject: [PATCH] Add a warning if a file will be overwritten. https://redmine.stoutner.com/issues/371 --- .../activities/ImportExportActivity.java | 244 +++++++++++++++--- .../activities/LogcatActivity.java | 14 +- .../activities/MainWebViewActivity.java | 6 +- .../privacybrowser/dialogs/OpenDialog.java | 71 +++-- .../dialogs/SaveLogcatDialog.java | 66 +++-- .../dialogs/SaveWebpageDialog.java | 66 +++-- .../dialogs/ViewSslCertificateDialog.java | 3 +- .../import_export_coordinatorlayout.xml | 23 +- app/src/main/res/layout/open_dialog.xml | 14 +- app/src/main/res/layout/save_dialog.xml | 14 +- app/src/main/res/values/strings.xml | 4 +- 11 files changed, 400 insertions(+), 125 deletions(-) diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java index ca4703af..df41cf31 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java @@ -1,5 +1,5 @@ /* - * Copyright © 2018-2019 Soren Stoutner . + * Copyright © 2018-2020 Soren Stoutner . * * This file is part of 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; diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.java index c38181a6..9f433632 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Soren Stoutner . + * Copyright © 2019-2020 Soren Stoutner . * * This file is part of 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); } } } diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java index 1904673b..4cef6813 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -1,5 +1,5 @@ /* - * Copyright © 2015-2019 Soren Stoutner . + * Copyright © 2015-2020 Soren Stoutner . * * Download cookie code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner . * @@ -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); } } } diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.java index b478d726..2d6a7761 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Soren Stoutner . + * Copyright © 2019-2020 Soren Stoutner . * * This file is part of 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; } diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveLogcatDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveLogcatDialog.java index b49958eb..9e27f529 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveLogcatDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveLogcatDialog.java @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2019 Soren Stoutner . + * Copyright © 2016-2020 Soren Stoutner . * * This file is part of 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; } diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveWebpageDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveWebpageDialog.java index 72d0f20a..7e4250a0 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveWebpageDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveWebpageDialog.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Soren Stoutner . + * Copyright © 2019-2020 Soren Stoutner . * * This file is part of 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; } diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java index d687546d..b7f0b487 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2019 Soren Stoutner . + * Copyright © 2016-2020 Soren Stoutner . * * This file is part of 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); diff --git a/app/src/main/res/layout/import_export_coordinatorlayout.xml b/app/src/main/res/layout/import_export_coordinatorlayout.xml index 29b6fe89..b6eebd1f 100644 --- a/app/src/main/res/layout/import_export_coordinatorlayout.xml +++ b/app/src/main/res/layout/import_export_coordinatorlayout.xml @@ -1,7 +1,7 @@ + + + + + + + + + +