From: Soren Stoutner Date: Thu, 20 Aug 2026 19:49:20 +0000 (-0700) Subject: Display the file name under the URI. https://redmine.stoutner.com/issues/1159 X-Git-Url: https://gitweb.stoutner.com/?a=commitdiff_plain;h=fff7330c2100515431be1f56f64465cd0f63e232;p=PrivacyBrowserAndroid.git Display the file name under the URI. https://redmine.stoutner.com/issues/1159 --- diff --git a/app/build.gradle b/app/build.gradle index 7686b53b..c9f75cc4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -81,7 +81,7 @@ dependencies { implementation "androidx.activity:activity-ktx:1.13.0" implementation 'androidx.arch.core:core-common:2.2.0' implementation 'androidx.arch.core:core-runtime:2.2.0' - implementation 'androidx.appcompat:appcompat:1.7.1' + implementation 'androidx.appcompat:appcompat:1.8.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.coordinatorlayout:coordinatorlayout:1.3.0' implementation 'androidx.core:core-ktx:1.19.0' @@ -90,7 +90,7 @@ dependencies { implementation 'androidx.preference:preference-ktx:1.2.1' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0' implementation 'androidx.viewpager:viewpager:1.1.0' - implementation 'androidx.webkit:webkit:1.16.0' + implementation 'androidx.webkit:webkit:1.17.0' // Include the Kotlin standard library. This should be the same version number listed in project build.gradle. implementation 'org.jetbrains.kotlin:kotlin-stdlib:2.2.10' diff --git a/app/src/main/assets/de/about_permissions.html b/app/src/main/assets/de/about_permissions.html index b20c81f2..45eb02d4 100644 --- a/app/src/main/assets/de/about_permissions.html +++ b/app/src/main/assets/de/about_permissions.html @@ -2,9 +2,9 @@ SPDX-License-Identifier: GPL-3.0-or-later SPDX-FileCopyrightText: 2016-2018, 2020-2022, 2025-2026 Soren Stoutner - Translation 2016 Aaron Gerlach . Copyright assigned to Soren Stoutner . + Translation 2019, 2022, 2026 Bernhard G. Keller. Copyright assigned to Soren Stoutner . Translation 2018 Stefan Erhardt. Copyright assigned to Soren Stoutner . - Translation 2019, 2022 Bernhard G. Keller. Copyright assigned to Soren Stoutner . + Translation 2016 Aaron Gerlach . Copyright assigned to Soren Stoutner . This file is part of Privacy Browser Android . @@ -43,8 +43,8 @@

Geräte in der Nähe

android.permission.ACCESS_LOCAL_NETWORK

-

This is a dangerous permission, meaning users must manually enable it if they desire the functionality. It was added in API 37 (Android 17). - It is required to load web pages on the Local Area Network (LAN), like the web interface of a wireless router.

+

Dies ist eine gefährliche Berechtigung, sodass sie vom Benutzer manuell aktiviert werden muss, wenn sie benötigt wird. + Sie wurde mit API 37 (Android 17) eingeführt und wird benötigt, um Web-Seiten in einem lokalen Netzwerk (LAN) zu laden, wie z.B. das Web-Interface eines WLAN-Routers o.ä.

com.stoutner.privacybrowser.standard.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION

diff --git a/app/src/main/assets/de/guide_requests.html b/app/src/main/assets/de/guide_requests.html index 51d079da..e5bb3a5e 100644 --- a/app/src/main/assets/de/guide_requests.html +++ b/app/src/main/assets/de/guide_requests.html @@ -77,14 +77,15 @@ Eine detailiertere Beschreibung, wie die Filterlisten-Einträge verarbeitet werden, ist unter stoutner.com verfügbar.

-

LAN Requests

+

Lokale Netzwerk-Anfragen

-

Beginning with Android 17 (API 37), Android’s WebView cannot make requests to devices on the LAN (Local Area Network) unless the - Nearby Devices permission has been granted. - This is a dangerous permission that must be explicitly enabled by the user. Devices on the LAN include everything that is on the same subnet as Privacy Browser, like the web interface of a wireless router. - Privacy Browser does not prompt for this permission, and there is no need for users to enable it if they only want to browse the general internet. - Users who need to be able to load web pages from the LAN can grant this permission in the OS settings for Privacy Browser.

+

Seit Android 17 (API 37), kann Android’s WebView keine Anfragen an Geräte im LAN (Local Area Network) senden, sofern dafür nicht die Berechtigung + Geräte in der Nähe (Nearby Devices) gewährt wurde. + Dies ist eine gefährliche Berechtigung, welche vom Benutzer explizit aktiviert werden muss. + Geräte im LAN beinhalten Alles im selben Subnet, in dem sich auch Privacy Browser befindet, wie z.B. das Web-Interface eines WLAN-Routers. + Privacy Browser fordert diese Berechtigung nicht ein und es ist nicht notwendig, sie zu aktivieren, um lediglich das allgemeinen Internet zu nutzen. + Benutzer, die Web-Seiten im LAN nutzen wollen, können diese Berechtigung in den Betriebssystem-Einstellungen von Privacy Browsergewähren.

- Nearby Devices Permission + Berechtigung für Geräte in der Nähe diff --git a/app/src/main/assets/en/guide_requests.html b/app/src/main/assets/en/guide_requests.html index f6b34d7e..df4097ff 100644 --- a/app/src/main/assets/en/guide_requests.html +++ b/app/src/main/assets/en/guide_requests.html @@ -73,7 +73,7 @@ A more detailed description of how the filter list entries are processed is available at stoutner.com.

-

LAN Requests

+

LAN Requests

Beginning with Android 17 (API 37), Android’s WebView cannot make requests to devices on the LAN (Local Area Network) unless the Nearby Devices permission has been granted. diff --git a/app/src/main/assets/es/about_permissions.html b/app/src/main/assets/es/about_permissions.html index b5efdb5c..02c7a015 100644 --- a/app/src/main/assets/es/about_permissions.html +++ b/app/src/main/assets/es/about_permissions.html @@ -2,7 +2,7 @@ SPDX-License-Identifier: GPL-3.0-or-later SPDX-FileCopyrightText: 2016-2018, 2020-2022, 2025-2026 Soren Stoutner - Translation 2017-2018, 2022 Jose A. León. Copyright assigned to Soren Stoutner . + Translation 2017-2018, 2022, 2026 Jose A. León. Copyright assigned to Soren Stoutner . This file is part of Privacy Browser Android . @@ -41,8 +41,8 @@

Dispositivos cercanos

android.permission.ACCESS_LOCAL_NETWORK

-

This is a dangerous permission, meaning users must manually enable it if they desire the functionality. It was added in API 37 (Android 17). - It is required to load web pages on the Local Area Network (LAN), like the web interface of a wireless router.

+

Este es un permiso peligroso, lo que significa que los usuarios deben activarlo manualmente si desean esta funcionalidad. Fue añadido en la API 37 (Android 17). + Es necesario para cargar páginas web en la red de área local (LAN), como la interfaz web de un router inalámbrico.

com.stoutner.privacybrowser.standard.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION

diff --git a/app/src/main/assets/es/guide_requests.html b/app/src/main/assets/es/guide_requests.html index 50cf59d3..82470147 100644 --- a/app/src/main/assets/es/guide_requests.html +++ b/app/src/main/assets/es/guide_requests.html @@ -77,14 +77,15 @@ Encontrará una descripción más detallada de cómo se procesan las entradas de la lista de filtros en stoutner.com.

-

LAN Requests

+

Solicitudes de LAN

-

Beginning with Android 17 (API 37), Android’s WebView cannot make requests to devices on the LAN (Local Area Network) unless the - Nearby Devices permission has been granted. - This is a dangerous permission that must be explicitly enabled by the user. Devices on the LAN include everything that is on the same subnet as Privacy Browser, like the web interface of a wireless router. - Privacy Browser does not prompt for this permission, and there is no need for users to enable it if they only want to browse the general internet. - Users who need to be able to load web pages from the LAN can grant this permission in the OS settings for Privacy Browser.

+

A partir de Android 17 (API 37), WebView de Android no puede realizar solicitudes a dispositivos de la LAN (Red de Área Local) a menos que + el permiso Dispositivos Cercanos se haya concedido. + Este es un permiso peligroso que debe ser habilitado explícitamente por el usuario. + Los dispositivos de la LAN incluyen todos aquellos que están en la misma subred que Navegador Privado, como la interfaz web de un router inalámbrico. + Navegador Privado no solicita este permiso, y no hay necesidad de que los usuarios lo activen si solo desean navegar por internet en general. + Los usuarios que necesiten cargar páginas webs desde la LAN pueden conceder este permiso en la configuración del sistema operativo para Navegador Privado.

- Nearby Devices Permission + Permiso de Dispositivos Cercanos diff --git a/app/src/main/assets/fr/guide_requests.html b/app/src/main/assets/fr/guide_requests.html index f4e31174..3846de16 100644 --- a/app/src/main/assets/fr/guide_requests.html +++ b/app/src/main/assets/fr/guide_requests.html @@ -78,7 +78,7 @@ stoutner.com.

-

LAN Requests

+

LAN Requests

Beginning with Android 17 (API 37), Android’s WebView cannot make requests to devices on the LAN (Local Area Network) unless the Nearby Devices permission has been granted. diff --git a/app/src/main/assets/it/about_permissions.html b/app/src/main/assets/it/about_permissions.html index 5aa290db..4449eeea 100644 --- a/app/src/main/assets/it/about_permissions.html +++ b/app/src/main/assets/it/about_permissions.html @@ -2,7 +2,7 @@ SPDX-License-Identifier: GPL-3.0-or-later SPDX-FileCopyrightText: 2017-2018, 2020-2022, 2025-2026 Soren Stoutner - Translation 2017-2018, 2022 Francesco Buratti. Copyright assigned to Soren Stoutner . + Translation 2017-2018, 2022, 2026 Francesco Buratti. Copyright assigned to Soren Stoutner . This file is part of Privacy Browser Android . @@ -42,8 +42,8 @@

Dispositivi nelle vicinanze

android.permission.ACCESS_LOCAL_NETWORK

-

This is a dangerous permission, meaning users must manually enable it if they desire the functionality. It was added in API 37 (Android 17). - It is required to load web pages on the Local Area Network (LAN), like the web interface of a wireless router.

+

Si tratta di una autorizzazione pericolosa, per cui gli utenti devono abilitarla manualmente nel caso vogliano utilizzare questa funzionalità. E' stata aggiunta con la API 37 (Android 17). + E' richiesta per caricare pagine web nella Local Area Network (LAN), come l'interfaccia web di un router wireless.

com.stoutner.privacybrowser.standard.AUTORIZZAZIONE_DYNAMIC_RECEIVER_NON_ESPORTATA

diff --git a/app/src/main/assets/it/guide_requests.html b/app/src/main/assets/it/guide_requests.html index f5e12f7d..b3264538 100644 --- a/app/src/main/assets/it/guide_requests.html +++ b/app/src/main/assets/it/guide_requests.html @@ -78,13 +78,14 @@ Una descrizione più dettagliata di come le liste dei filtri sono processate è disponibile su stoutner.com.

-

LAN Requests

- -

Beginning with Android 17 (API 37), Android’s WebView cannot make requests to devices on the LAN (Local Area Network) unless the - Nearby Devices permission has been granted. - This is a dangerous permission that must be explicitly enabled by the user. Devices on the LAN include everything that is on the same subnet as Privacy Browser, like the web interface of a wireless router. - Privacy Browser does not prompt for this permission, and there is no need for users to enable it if they only want to browse the general internet. - Users who need to be able to load web pages from the LAN can grant this permission in the OS settings for Privacy Browser.

+

Richieste LAN

+ +

A partire da Android 17 (API 37), la webview di Android non può inviare richieste a dispositivi sulla LAN (Local Area Network) a meno che l'autorizzazione + Dispositivi nelle vicinanze non sia stata garantita. + Si tratta di una autorizzazione pericolosa che deve essere abilitata esplicitamente dall'utente. + I dispositivi presenti sulla LAN includono tutto ciò che è presente sulla stessa subnet di Privacy Browser, come l'interfaccia web di un router wireless. + Privacy Browser non chiede questa autorizzazione, e non c'è nessuna necessità per gli utenti di abilitarla se tutto ciò che fogliono fare è solo navigare su internet. + Gli utenti che vogliono essere in grado di caricare pagine web dalla LAN possono garantire questa autorizzazione nelle impostazioni del sistema operativo per Privacy Browser.

Nearby Devices Permission diff --git a/app/src/main/assets/pt-rBR/guide_requests.html b/app/src/main/assets/pt-rBR/guide_requests.html index 7e34b2a2..fb394e02 100644 --- a/app/src/main/assets/pt-rBR/guide_requests.html +++ b/app/src/main/assets/pt-rBR/guide_requests.html @@ -77,7 +77,7 @@ A more detailed description of how the filter list entries are processed is available at stoutner.com.

-

LAN Requests

+

LAN Requests

Beginning with Android 17 (API 37), Android’s WebView cannot make requests to devices on the LAN (Local Area Network) unless the Nearby Devices permission has been granted. diff --git a/app/src/main/assets/ru/about_permissions.html b/app/src/main/assets/ru/about_permissions.html index e2f9bd67..0a64e5a3 100644 --- a/app/src/main/assets/ru/about_permissions.html +++ b/app/src/main/assets/ru/about_permissions.html @@ -39,8 +39,8 @@

Устройства поблизости

android.permission.ACCESS_LOCAL_NETWORK

-

This is a dangerous permission, meaning users must manually enable it if they desire the functionality. It was added in API 37 (Android 17). - It is required to load web pages on the Local Area Network (LAN), like the web interface of a wireless router.

+

Это опасное разрешение, то есть пользователи должны включить его вручную, если им нужна эта функциональность. Оно было добавлено в API 37 (Android 17). + Оно требуется для загрузки веб-страниц в локальной сети (LAN), например веб-интерфейса беспроводного роутера.

com.stoutner.privacybrowser.standard.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION

diff --git a/app/src/main/assets/ru/guide_requests.html b/app/src/main/assets/ru/guide_requests.html index 826778ff..4c424b87 100644 --- a/app/src/main/assets/ru/guide_requests.html +++ b/app/src/main/assets/ru/guide_requests.html @@ -76,14 +76,15 @@ Более подробное описание обработки записей фильтров доступно на странице stoutner.com.

-

LAN Requests

+

Запросы в локальной сети

-

Beginning with Android 17 (API 37), Android’s WebView cannot make requests to devices on the LAN (Local Area Network) unless the - Nearby Devices permission has been granted. - This is a dangerous permission that must be explicitly enabled by the user. Devices on the LAN include everything that is on the same subnet as Privacy Browser, like the web interface of a wireless router. - Privacy Browser does not prompt for this permission, and there is no need for users to enable it if they only want to browse the general internet. - Users who need to be able to load web pages from the LAN can grant this permission in the OS settings for Privacy Browser.

+

Начиная с Android 17 (API 37), WebView в Android не может отправлять запросы к устройствам в локальной сети (LAN), если не предоставлено разрешение + «Устройства поблизости». + Это опасное разрешение, которое пользователь должен включить явным образом. + К устройствам в локальной сети относится все, что находится в той же подсети, что и Privacy Browser, например веб-интерфейс беспроводного роутера. + Privacy Browser не запрашивает это разрешение, и пользователям не нужно его включать, если они хотят просто просматривать интернет. + Пользователи, которым нужна возможность загружать веб-страницы из локальной сети, могут предоставить это разрешение для Privacy Browser в настройках ОС.

- Nearby Devices Permission + Разрешение Устройства поблизости diff --git a/app/src/main/assets/tr/guide_requests.html b/app/src/main/assets/tr/guide_requests.html index 05e51f0f..9882ff2c 100644 --- a/app/src/main/assets/tr/guide_requests.html +++ b/app/src/main/assets/tr/guide_requests.html @@ -74,7 +74,7 @@ A more detailed description of how the filter list entries are processed is available at stoutner.com.

-

LAN Requests

+

LAN Requests

Beginning with Android 17 (API 37), Android’s WebView cannot make requests to devices on the LAN (Local Area Network) unless the Nearby Devices permission has been granted. diff --git a/app/src/main/assets/zh-rCN/guide_requests.html b/app/src/main/assets/zh-rCN/guide_requests.html index 4cbded69..3d813560 100644 --- a/app/src/main/assets/zh-rCN/guide_requests.html +++ b/app/src/main/assets/zh-rCN/guide_requests.html @@ -74,7 +74,7 @@ A more detailed description of how the filter list entries are processed is available at stoutner.com.

-

LAN Requests

+

LAN Requests

Beginning with Android 17 (API 37), Android’s WebView cannot make requests to devices on the LAN (Local Area Network) unless the Nearby Devices permission has been granted. diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.kt b/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.kt index 41953397..ae0e7897 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.kt @@ -24,6 +24,7 @@ import android.content.pm.PackageManager import android.net.Uri import android.os.Bundle import android.os.Handler +import android.provider.OpenableColumns import android.text.Editable import android.text.TextWatcher import android.view.View @@ -61,7 +62,6 @@ import java.io.File import java.io.FileInputStream import java.io.FileNotFoundException import java.io.FileOutputStream -import java.lang.Exception import java.nio.charset.StandardCharsets import java.security.MessageDigest import java.security.SecureRandom @@ -72,6 +72,7 @@ import javax.crypto.CipherInputStream import javax.crypto.CipherOutputStream import javax.crypto.spec.GCMParameterSpec import javax.crypto.spec.SecretKeySpec + import kotlin.system.exitProcess // Define the encryption constants. @@ -80,36 +81,36 @@ private const val PASSWORD_ENCRYPTION = 1 private const val OPENPGP_ENCRYPTION = 2 // Define the saved instance state constants. -private const val ENCRYPTION_PASSWORD_TEXTINPUTLAYOUT_VISIBILITY = "A" -private const val OPEN_KEYCHAIN_REQUIRED_TEXTVIEW_VISIBILITY = "B" -private const val SETTINGS_FILE_LOCATION_CARDVIEW_VISIBILITY = "C" -private const val SETTINGS_FILE_NAME_LINEARLAYOUT_VISIBILITY = "D" -private const val OPEN_KEYCHAIN_IMPORT_INSTRUCTIONS_TEXTVIEW_VISIBILITY = "E" -private const val SETTINGS_IMPORT_EXPORT_BUTTON_VISIBILITY = "F" -private const val SETTINGS_FILE_NAME_TEXT = "G" -private const val SETTINGS_IMPORT_EXPORT_BUTTON_TEXT = "H" -private const val BOOKMARKS_FILE_NAME_LINEARLAYOUT_VISIBILITY = "I" -private const val BOOKMARKS_IMPORT_EXPORT_BUTTON_VISIBILITY = "J" -private const val BOOKMARKS_FILE_NAME_TEXT = "K" -private const val BOOKMARKS_IMPORT_EXPORT_BUTTON_TEXT = "L" +private const val BOOKMARKS_IMPORT_EXPORT_BUTTON_STRING = "bookmarks_import_export_button_string" +private const val BOOKMARKS_IMPORT_EXPORT_BUTTON_VISIBILITY = "bookmarks_import_export_button_visibility" +private const val BOOKMARKS_URI_LINEARLAYOUT_VISIBILITY = "bookmarks_uri_linearlayout_visibility" +private const val BOOKMARKS_URI_STRING = "booksmarks_uri_string" +private const val ENCRYPTION_PASSWORD_TEXTINPUTLAYOUT_VISIBILITY = "encryption_password_textinputlayout_visibility" +private const val OPEN_KEYCHAIN_IMPORT_INSTRUCTIONS_TEXTVIEW_VISIBILITY = "open_keychian_import_instructions_textview_visibility" +private const val OPEN_KEYCHAIN_REQUIRED_TEXTVIEW_VISIBILITY = "open_keychain_required_textview_visibilty" +private const val SETTINGS_FILE_LOCATION_CARDVIEW_VISIBILITY = "settings_file_location_cardview_visibility" +private const val SETTINGS_IMPORT_EXPORT_BUTTON_TEXT = "settings_import_export_button_text" +private const val SETTINGS_IMPORT_EXPORT_BUTTON_VISIBILITY = "settings_import_export_button_visibility" +private const val SETTINGS_URI_LINEARLAYOUT_VISIBILITY = "settings_uri_linearlayout_visibility" +private const val SETTINGS_URI_STRING = "settings_uri_string" class ImportExportActivity : AppCompatActivity() { // Define the class views. - private lateinit var scrollView: ScrollView - private lateinit var encryptionSpinner: Spinner - private lateinit var encryptionPasswordTextInputLayout: TextInputLayout + private lateinit var bookmarksUriEditText: EditText + private lateinit var bookmarksImportExportButton: Button + private lateinit var bookmarksImportRadioButton: RadioButton + private lateinit var bookmarksUriLinearLayout: LinearLayout private lateinit var encryptionPasswordEditText: EditText + private lateinit var encryptionPasswordTextInputLayout: TextInputLayout + private lateinit var encryptionSpinner: Spinner + private lateinit var openKeychainImportInstructionsTextView: TextView private lateinit var openKeychainRequiredTextView: TextView + private lateinit var scrollView: ScrollView private lateinit var settingsFileLocationCardView: CardView - private lateinit var settingsImportRadioButton: RadioButton - private lateinit var settingsFileNameLinearLayout: LinearLayout - private lateinit var settingsFileNameEditText: EditText - private lateinit var openKeychainImportInstructionsTextView: TextView private lateinit var settingsImportExportButton: Button - private lateinit var bookmarksImportRadioButton: RadioButton - private lateinit var bookmarksFileNameLinearLayout: LinearLayout - private lateinit var bookmarksFileNameEditText: EditText - private lateinit var bookmarksImportExportButton: Button + private lateinit var settingsImportRadioButton: RadioButton + private lateinit var settingsUriEditText: EditText + private lateinit var settingsUriLinearLayout: LinearLayout // Define the class variables. private lateinit var fileProviderDirectory: File @@ -121,28 +122,28 @@ class ImportExportActivity : AppCompatActivity() { private val settingsBrowseForImportActivityResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { fileUri: Uri? -> // Only do something if the user didn't press back from the file picker. if (fileUri != null) { - // Get the file name string from the URI. - val fileNameString = fileUri.toString() + // Convert the URI to a string. + val fileUriString = fileUri.toString() - // Set the settings file name text. - settingsFileNameEditText.setText(fileNameString) + // Set the settings URI text. + settingsUriEditText.setText(fileUriString) - // Move the cursor to the end of the file name edit text. - settingsFileNameEditText.setSelection(fileNameString.length) + // Move the cursor to the end of the URI edit text. + settingsUriEditText.setSelection(fileUriString.length) } } private val settingsBrowseForExportActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("*/*")) { fileUri: Uri? -> // Only do something if the user didn't press back from the file picker. if (fileUri != null) { - // Get the file name string from the URI. - val fileNameString = fileUri.toString() + // Convert the URI to a string. + val fileUriString = fileUri.toString() - // Set the settings file name text. - settingsFileNameEditText.setText(fileNameString) + // Set the settings URI text. + settingsUriEditText.setText(fileUriString) - // Move the cursor to the end of the file name edit text. - settingsFileNameEditText.setSelection(fileNameString.length) + // Move the cursor to the end of the Uri edit text. + settingsUriEditText.setSelection(fileUriString.length) } } @@ -152,11 +153,11 @@ class ImportExportActivity : AppCompatActivity() { // Get the file name string from the URI. val fileNameString = fileUri.toString() - // Set the bookmarks file name text. - bookmarksFileNameEditText.setText(fileNameString) + // Set the bookmarks URI text. + bookmarksUriEditText.setText(fileNameString) - // Move the cursor to the end of the file name edit text. - bookmarksFileNameEditText.setSelection(fileNameString.length) + // Move the cursor to the end of the URI edit text. + bookmarksUriEditText.setSelection(fileNameString.length) // Scroll to the bottom. scrollView.post { @@ -171,11 +172,11 @@ class ImportExportActivity : AppCompatActivity() { // Get the file name string from the URI. val fileNameString = fileUri.toString() - // Set the bookmarks file name text. - bookmarksFileNameEditText.setText(fileNameString) + // Set the bookmarks URI text. + bookmarksUriEditText.setText(fileNameString) // Move the cursor to the end of the file name edit text. - bookmarksFileNameEditText.setSelection(fileNameString.length) + bookmarksUriEditText.setSelection(fileNameString.length) // Scroll to the bottom. scrollView.post { @@ -255,13 +256,15 @@ class ImportExportActivity : AppCompatActivity() { settingsFileLocationCardView = findViewById(R.id.settings_file_location_cardview) settingsImportRadioButton = findViewById(R.id.settings_import_radiobutton) val settingsExportRadioButton = findViewById(R.id.settings_export_radiobutton) - settingsFileNameLinearLayout = findViewById(R.id.settings_file_name_linearlayout) - settingsFileNameEditText = findViewById(R.id.settings_file_name_edittext) + settingsUriLinearLayout = findViewById(R.id.settings_uri_linearlayout) + settingsUriEditText = findViewById(R.id.settings_uri_edittext) + val settingsFileNameTextView = findViewById(R.id.settings_file_name_textview) openKeychainImportInstructionsTextView = findViewById(R.id.openkeychain_import_instructions_textview) settingsImportExportButton = findViewById(R.id.settings_import_export_button) bookmarksImportRadioButton = findViewById(R.id.bookmarks_import_radiobutton) - bookmarksFileNameLinearLayout = findViewById(R.id.bookmarks_file_name_linearlayout) - bookmarksFileNameEditText = findViewById(R.id.bookmarks_file_name_edittext) + bookmarksUriLinearLayout = findViewById(R.id.bookmarks_uri_linearlayout) + bookmarksUriEditText = findViewById(R.id.bookmarks_uri_edittext) + val bookmarksFileNameTextView = findViewById(R.id.bookmarks_file_name_textview) bookmarksImportExportButton = findViewById(R.id.bookmarks_import_export_button) // Create an array adapter for the spinner. @@ -286,16 +289,16 @@ class ImportExportActivity : AppCompatActivity() { // Show the file location card. settingsFileLocationCardView.visibility = View.VISIBLE - // Show the file name linear layout if either import or export is checked. + // Show the URI linear layout if either import or export is checked. if (settingsImportRadioButton.isChecked || settingsExportRadioButton.isChecked) - settingsFileNameLinearLayout.visibility = View.VISIBLE + settingsUriLinearLayout.visibility = View.VISIBLE // Reset the text of the import button, which may have been changed to `Decrypt`. if (settingsImportRadioButton.isChecked) settingsImportExportButton.setText(R.string.import_button) - // Clear the file name edit text. - settingsFileNameEditText.text.clear() + // Clear the texts. + settingsUriEditText.text.clear() // Disable the import/export button. settingsImportExportButton.isEnabled = false @@ -312,16 +315,16 @@ class ImportExportActivity : AppCompatActivity() { // Show the file location card. settingsFileLocationCardView.visibility = View.VISIBLE - // Show the file name linear layout if either import or export is checked. + // Show the URI linear layout if either import or export is checked. if (settingsImportRadioButton.isChecked || settingsExportRadioButton.isChecked) - settingsFileNameLinearLayout.visibility = View.VISIBLE + settingsUriLinearLayout.visibility = View.VISIBLE // Reset the text of the import button, which may have been changed to `Decrypt`. if (settingsImportRadioButton.isChecked) settingsImportExportButton.setText(R.string.import_button) - // Clear the file name edit text. - settingsFileNameEditText.text.clear() + // Clear the texts. + settingsUriEditText.text.clear() // Disable the import/export button. settingsImportExportButton.isEnabled = false @@ -338,21 +341,21 @@ class ImportExportActivity : AppCompatActivity() { // Update the layout based on the checked radio button. if (settingsImportRadioButton.isChecked) { - // Show the file name linear layout and the OpenKeychain import instructions. - settingsFileNameLinearLayout.visibility = View.VISIBLE + // Show the URI linear layout and the OpenKeychain import instructions. + settingsUriLinearLayout.visibility = View.VISIBLE openKeychainImportInstructionsTextView.visibility = View.VISIBLE // Set the text of the import button to be `Decrypt`. settingsImportExportButton.setText(R.string.decrypt) - // Clear the file name edit text. - settingsFileNameEditText.text.clear() + // Clear the texts. + settingsUriEditText.text.clear() // Disable the import/export button. settingsImportExportButton.isEnabled = false } else if (settingsExportRadioButton.isChecked) { - // Hide the file name linear layout and the OpenKeychain import instructions. - settingsFileNameLinearLayout.visibility = View.GONE + // Hide the URI linear layout and the OpenKeychain import instructions. + settingsUriLinearLayout.visibility = View.GONE openKeychainImportInstructionsTextView.visibility = View.GONE // Enable the export button. @@ -383,13 +386,13 @@ class ImportExportActivity : AppCompatActivity() { } override fun afterTextChanged(s: Editable) { - // Enable the import/export button if both the file string and the password are populated. - settingsImportExportButton.isEnabled = settingsFileNameEditText.text.toString().isNotEmpty() && encryptionPasswordEditText.text.toString().isNotEmpty() + // Enable the import/export button if both the URI string and the password are populated. + settingsImportExportButton.isEnabled = settingsUriEditText.text.toString().isNotEmpty() && encryptionPasswordEditText.text.toString().isNotEmpty() } }) - // Update the UI when the settings file name edit text changes. - settingsFileNameEditText.addTextChangedListener(object : TextWatcher { + // Update the UI when the settings URI edit text changes. + settingsUriEditText.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { // Do nothing. } @@ -398,20 +401,65 @@ class ImportExportActivity : AppCompatActivity() { // Do nothing. } - override fun afterTextChanged(s: Editable) { + override fun afterTextChanged(editable: Editable) { // Adjust the UI according to the encryption spinner position. if (encryptionSpinner.selectedItemPosition == PASSWORD_ENCRYPTION) { - // Enable the settings import/export button if both the file name and the password are populated. - settingsImportExportButton.isEnabled = settingsFileNameEditText.text.toString().isNotEmpty() && encryptionPasswordEditText.text.toString().isNotEmpty() + // Enable the settings import/export button if both the URI and the password are populated. + settingsImportExportButton.isEnabled = settingsUriEditText.text.toString().isNotEmpty() && encryptionPasswordEditText.text.toString().isNotEmpty() } else { - // Enable the settings import/export button if the file name is populated. - settingsImportExportButton.isEnabled = settingsFileNameEditText.text.toString().isNotEmpty() + // Enable the settings import/export button if the URI is populated. + settingsImportExportButton.isEnabled = settingsUriEditText.text.toString().isNotEmpty() + } + + // Update the file name text view. + if (editable.isEmpty()) { // The URI is empty. + // Hide the settings file name text view. + settingsFileNameTextView.visibility = View.GONE + } else { // The URI is not empty. + // Create a file name URI. + val fileNameUri = editable.toString().toUri() + + // Create a projection string array (the list of columns to request in the content resolver query). + val projectionStringArray = arrayOf(OpenableColumns.DISPLAY_NAME) + + // Try to get the file name. This can fail if the URI has been edited in such a way that it doesn't match a recent API request by the browse button. + try { + // Get the file name cursor. + val fileNameCursor = contentResolver.query(fileNameUri, projectionStringArray, null, null) + + // Initialize a file name string. + var fileNameString = "" + + // Get the file name from the cursor. + if ((fileNameCursor != null) && (fileNameCursor.count != 0)) { + // Move to the first entry in the cursor. + fileNameCursor.moveToFirst() + + // Populate the file name string. + fileNameString = fileNameCursor.getString(0) + + // Close the cursor. + fileNameCursor.close() + } + + // Populate the file name text view if the file name string is not empty. + if (fileNameString.isNotEmpty()) { + // Populate the settings file name text. + settingsFileNameTextView.text = fileNameString + + // Show the settings file name text view. + settingsFileNameTextView.visibility = View.VISIBLE + } + } catch (_: Exception) { // The URI could not be converted into a file name. + // Hide the settings file name text view. + settingsFileNameTextView.visibility = View.GONE + } } } }) - // Update the UI when the bookmarks file name edit text changes. - bookmarksFileNameEditText.addTextChangedListener(object : TextWatcher { + // Update the UI when the bookmarks URI edit text changes. + bookmarksUriEditText.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { // Do nothing. } @@ -420,9 +468,54 @@ class ImportExportActivity : AppCompatActivity() { // Do nothing. } - override fun afterTextChanged(s: Editable) { + override fun afterTextChanged(editable: Editable) { // Enable the bookmarks import/export button if the file name is populated. - bookmarksImportExportButton.isEnabled = bookmarksFileNameEditText.text.toString().isNotEmpty() + bookmarksImportExportButton.isEnabled = bookmarksUriEditText.text.toString().isNotEmpty() + + // Update the bookmarks file name text view. + if (editable.isEmpty()) { // The URI is empty. + // Hide the bookmarks file name text view. + bookmarksFileNameTextView.visibility = View.GONE + } else { // The URI is not empty. + // Create a file name URI. + val fileNameUri = editable.toString().toUri() + + // Create a projection string array (the list of columns to request in the content resolver query). + val projectionStringArray = arrayOf(OpenableColumns.DISPLAY_NAME) + + // Try to get the file name. This can fail if the URI has been edited in such a way that it doesn't match a recent API request by the browse button. + try { + // Get the file name cursor. + val fileNameCursor = contentResolver.query(fileNameUri, projectionStringArray, null, null) + + // Initialize a file name string. + var fileNameString = "" + + // Get the file name from the cursor. + if ((fileNameCursor != null) && (fileNameCursor.count != 0)) { + // Move to the first entry in the cursor. + fileNameCursor.moveToFirst() + + // Populate the file name string. + fileNameString = fileNameCursor.getString(0) + + // Close the cursor. + fileNameCursor.close() + } + + // Populate the file name text view if the file name string is not empty. + if (fileNameString.isNotEmpty()) { + // Populate the bookmarks file name text. + bookmarksFileNameTextView.text = fileNameString + + // Show the bookmarks file name text view. + bookmarksFileNameTextView.visibility = View.VISIBLE + } + } catch (_: Exception) { // The URI could not be converted into a file name. + // Hide the bookmarks file name text view. + bookmarksFileNameTextView.visibility = View.GONE + } + } } }) @@ -431,27 +524,27 @@ class ImportExportActivity : AppCompatActivity() { // Initially hide the unneeded views. encryptionPasswordTextInputLayout.visibility = View.GONE openKeychainRequiredTextView.visibility = View.GONE - settingsFileNameLinearLayout.visibility = View.GONE + settingsUriLinearLayout.visibility = View.GONE openKeychainImportInstructionsTextView.visibility = View.GONE settingsImportExportButton.visibility = View.GONE - bookmarksFileNameLinearLayout.visibility = View.GONE + bookmarksUriLinearLayout.visibility = View.GONE bookmarksImportExportButton.visibility = View.GONE } else { // The app has been restarted. // Restore the visibility of the views. encryptionPasswordTextInputLayout.visibility = savedInstanceState.getInt(ENCRYPTION_PASSWORD_TEXTINPUTLAYOUT_VISIBILITY) openKeychainRequiredTextView.visibility = savedInstanceState.getInt(OPEN_KEYCHAIN_REQUIRED_TEXTVIEW_VISIBILITY) settingsFileLocationCardView.visibility = savedInstanceState.getInt(SETTINGS_FILE_LOCATION_CARDVIEW_VISIBILITY) - settingsFileNameLinearLayout.visibility = savedInstanceState.getInt(SETTINGS_FILE_NAME_LINEARLAYOUT_VISIBILITY) + settingsUriLinearLayout.visibility = savedInstanceState.getInt(SETTINGS_URI_LINEARLAYOUT_VISIBILITY) openKeychainImportInstructionsTextView.visibility = savedInstanceState.getInt(OPEN_KEYCHAIN_IMPORT_INSTRUCTIONS_TEXTVIEW_VISIBILITY) settingsImportExportButton.visibility = savedInstanceState.getInt(SETTINGS_IMPORT_EXPORT_BUTTON_VISIBILITY) - bookmarksFileNameLinearLayout.visibility = savedInstanceState.getInt(BOOKMARKS_FILE_NAME_LINEARLAYOUT_VISIBILITY) + bookmarksUriLinearLayout.visibility = savedInstanceState.getInt(BOOKMARKS_URI_LINEARLAYOUT_VISIBILITY) bookmarksImportExportButton.visibility = savedInstanceState.getInt(BOOKMARKS_IMPORT_EXPORT_BUTTON_VISIBILITY) // Restore the text. - settingsFileNameEditText.post { settingsFileNameEditText.setText(savedInstanceState.getString(SETTINGS_FILE_NAME_TEXT)) } + settingsUriEditText.post { settingsUriEditText.setText(savedInstanceState.getString(SETTINGS_URI_STRING)) } settingsImportExportButton.text = savedInstanceState.getString(SETTINGS_IMPORT_EXPORT_BUTTON_TEXT) - bookmarksFileNameEditText.post { bookmarksFileNameEditText.setText(savedInstanceState.getString(BOOKMARKS_FILE_NAME_TEXT)) } - bookmarksImportExportButton.text = savedInstanceState.getString(BOOKMARKS_IMPORT_EXPORT_BUTTON_TEXT) + bookmarksUriEditText.post { bookmarksUriEditText.setText(savedInstanceState.getString(BOOKMARKS_URI_STRING)) } + bookmarksImportExportButton.text = savedInstanceState.getString(BOOKMARKS_IMPORT_EXPORT_BUTTON_STRING) } } @@ -463,17 +556,17 @@ class ImportExportActivity : AppCompatActivity() { outState.putInt(ENCRYPTION_PASSWORD_TEXTINPUTLAYOUT_VISIBILITY, encryptionPasswordTextInputLayout.visibility) outState.putInt(OPEN_KEYCHAIN_REQUIRED_TEXTVIEW_VISIBILITY, openKeychainRequiredTextView.visibility) outState.putInt(SETTINGS_FILE_LOCATION_CARDVIEW_VISIBILITY, settingsFileLocationCardView.visibility) - outState.putInt(SETTINGS_FILE_NAME_LINEARLAYOUT_VISIBILITY, settingsFileNameLinearLayout.visibility) + outState.putInt(SETTINGS_URI_LINEARLAYOUT_VISIBILITY, settingsUriLinearLayout.visibility) outState.putInt(OPEN_KEYCHAIN_IMPORT_INSTRUCTIONS_TEXTVIEW_VISIBILITY, openKeychainImportInstructionsTextView.visibility) outState.putInt(SETTINGS_IMPORT_EXPORT_BUTTON_VISIBILITY, settingsImportExportButton.visibility) - outState.putInt(BOOKMARKS_FILE_NAME_LINEARLAYOUT_VISIBILITY, bookmarksFileNameLinearLayout.visibility) + outState.putInt(BOOKMARKS_URI_LINEARLAYOUT_VISIBILITY, bookmarksUriLinearLayout.visibility) outState.putInt(BOOKMARKS_IMPORT_EXPORT_BUTTON_VISIBILITY, bookmarksImportExportButton.visibility) // Save the text. - outState.putString(SETTINGS_FILE_NAME_TEXT, settingsFileNameEditText.text.toString()) + outState.putString(SETTINGS_URI_STRING, settingsUriEditText.text.toString()) outState.putString(SETTINGS_IMPORT_EXPORT_BUTTON_TEXT, settingsImportExportButton.text.toString()) - outState.putString(BOOKMARKS_FILE_NAME_TEXT, bookmarksFileNameEditText.text.toString()) - outState.putString(BOOKMARKS_IMPORT_EXPORT_BUTTON_VISIBILITY, bookmarksImportExportButton.text.toString()) + outState.putString(BOOKMARKS_URI_STRING, bookmarksUriEditText.text.toString()) + outState.putString(BOOKMARKS_IMPORT_EXPORT_BUTTON_STRING, bookmarksImportExportButton.text.toString()) } fun onClickBookmarksRadioButton(view: View) { @@ -487,11 +580,11 @@ class ImportExportActivity : AppCompatActivity() { } // Display the bookmarks views. - bookmarksFileNameLinearLayout.visibility = View.VISIBLE + bookmarksUriLinearLayout.visibility = View.VISIBLE bookmarksImportExportButton.visibility = View.VISIBLE - // Clear the bookmarks file name edit text. - bookmarksFileNameEditText.text.clear() + // Clear the bookmarks URI edit text. + bookmarksUriEditText.text.clear() // Disable the bookmarks import/export button. bookmarksImportExportButton.isEnabled = false @@ -521,11 +614,11 @@ class ImportExportActivity : AppCompatActivity() { } // Display the views. - settingsFileNameLinearLayout.visibility = View.VISIBLE + settingsUriLinearLayout.visibility = View.VISIBLE settingsImportExportButton.visibility = View.VISIBLE - // Clear the settings file name edit text. - settingsFileNameEditText.text.clear() + // Clear the settings texts. + settingsUriEditText.text.clear() // Disable the settings import/export button. settingsImportExportButton.isEnabled = false @@ -541,17 +634,17 @@ class ImportExportActivity : AppCompatActivity() { // Check to see if OpenPGP encryption is selected. if (encryptionSpinner.selectedItemPosition == OPENPGP_ENCRYPTION) { // OpenPGP encryption is selected. - // Hide the settings file name views. - settingsFileNameLinearLayout.visibility = View.GONE + // Hide the settings URI views. + settingsUriLinearLayout.visibility = View.GONE // Enable the settings export button. settingsImportExportButton.isEnabled = true } else { // OpenPGP encryption is not selected. - // Show the settings file name view. - settingsFileNameLinearLayout.visibility = View.VISIBLE + // Show the settings URI views. + settingsUriLinearLayout.visibility = View.VISIBLE - // Clear the settings file name edit text. - settingsFileNameEditText.text.clear() + // Clear the settings texts. + settingsUriEditText.text.clear() // Disable the settings import/export button. settingsImportExportButton.isEnabled = false @@ -588,19 +681,19 @@ class ImportExportActivity : AppCompatActivity() { // Instantiate the import/export bookmarks helper. val importExportBookmarksHelper = ImportExportBookmarksHelper() - // Get the file name string. - val fileNameString = bookmarksFileNameEditText.text.toString() + // Get the file URI string. + val fileUriString = bookmarksUriEditText.text.toString() // Check to see if import or export is selected. if (bookmarksImportRadioButton.isChecked) { // Import is selected. // Import the bookmarks. - importExportBookmarksHelper.importBookmarks(fileNameString, context = this, scrollView) + importExportBookmarksHelper.importBookmarks(fileUriString, context = this, scrollView) // Repopulate the bookmarks in the main WebView activity. MainWebViewActivity.restartFromBookmarksActivity = true } else { // Export is selected. // Export the bookmarks. - importExportBookmarksHelper.exportBookmarks(fileNameString, context = this, scrollView) + importExportBookmarksHelper.exportBookmarks(fileUriString, context = this, scrollView) } } @@ -613,15 +706,15 @@ class ImportExportActivity : AppCompatActivity() { // Initialize the import status string var importStatus = "" - // Get the file name string. - val fileNameString = settingsFileNameEditText.text.toString() + // Get the file URI string. + val fileUriString = settingsUriEditText.text.toString() // Import according to the encryption type. when (encryptionSpinner.selectedItemPosition) { NO_ENCRYPTION -> { try { // Get an input stream for the file name. - val inputStream = contentResolver.openInputStream(fileNameString.toUri())!! + val inputStream = contentResolver.openInputStream(fileUriString.toUri())!! // Import the unencrypted file. importStatus = importExportDatabaseHelper.importUnencrypted(inputStream, this) @@ -643,8 +736,8 @@ class ImportExportActivity : AppCompatActivity() { // Get the encryption password. val encryptionPasswordString = encryptionPasswordEditText.text.toString() - // Get an input stream for the file name. - val inputStream = contentResolver.openInputStream(fileNameString.toUri())!! + // Get an input stream for the file URI. + val inputStream = contentResolver.openInputStream(fileUriString.toUri())!! // Initialize a salt byte array. Salt protects against rainbow table attacks. val saltByteArray = ByteArray(32) @@ -762,8 +855,8 @@ class ImportExportActivity : AppCompatActivity() { // Create a temporary PGP encrypted import file output stream. val temporaryPgpEncryptedImportFileOutputStream = FileOutputStream(temporaryPgpEncryptedImportFile) - // Get an input stream for the file name. - val inputStream = contentResolver.openInputStream(fileNameString.toUri())!! + // Get an input stream for the file URI. + val inputStream = contentResolver.openInputStream(fileUriString.toUri())!! // Create a transfer byte array. val transferByteArray = ByteArray(1024) @@ -808,13 +901,13 @@ class ImportExportActivity : AppCompatActivity() { // Display a snack bar with the import error if it was unsuccessful. if (importStatus != IMPORT_SUCCESSFUL) - Snackbar.make(settingsFileNameEditText, getString(R.string.import_failed, importStatus), Snackbar.LENGTH_INDEFINITE).show() + Snackbar.make(settingsUriEditText, getString(R.string.import_failed, importStatus), Snackbar.LENGTH_INDEFINITE).show() } else { // Export is selected. // Export according to the encryption type. when (encryptionSpinner.selectedItemPosition) { NO_ENCRYPTION -> { - // Get the file name string. - val noEncryptionFileNameString = settingsFileNameEditText.text.toString() + // Get the file URI string. + val noEncryptionFileNameString = settingsUriEditText.text.toString() try { // Get the export file output stream, truncating any existing content. @@ -828,12 +921,12 @@ class ImportExportActivity : AppCompatActivity() { // Display an export disposition snackbar. if (noEncryptionExportStatus == EXPORT_SUCCESSFUL) - Snackbar.make(settingsFileNameEditText, getString(R.string.export_successful), Snackbar.LENGTH_SHORT).show() + Snackbar.make(settingsUriEditText, getString(R.string.export_successful), Snackbar.LENGTH_SHORT).show() else - Snackbar.make(settingsFileNameEditText, getString(R.string.export_failed, noEncryptionExportStatus), Snackbar.LENGTH_INDEFINITE).show() + Snackbar.make(settingsUriEditText, getString(R.string.export_failed, noEncryptionExportStatus), Snackbar.LENGTH_INDEFINITE).show() } catch (fileNotFoundException: FileNotFoundException) { // Display a snackbar with the exception. - Snackbar.make(settingsFileNameEditText, getString(R.string.export_failed, fileNotFoundException), Snackbar.LENGTH_INDEFINITE).show() + Snackbar.make(settingsUriEditText, getString(R.string.export_failed, fileNotFoundException), Snackbar.LENGTH_INDEFINITE).show() } } @@ -906,7 +999,7 @@ class ImportExportActivity : AppCompatActivity() { cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmParameterSpec) // Get the file name string. - val passwordEncryptionFileNameString = settingsFileNameEditText.text.toString() + val passwordEncryptionFileNameString = settingsUriEditText.text.toString() // Get the export file output stream, truncating any existing content. val exportFileOutputStream = contentResolver.openOutputStream(passwordEncryptionFileNameString.toUri(), "wt")!! @@ -947,12 +1040,12 @@ class ImportExportActivity : AppCompatActivity() { // Display an export disposition snackbar. if (passwordEncryptionExportStatus == EXPORT_SUCCESSFUL) - Snackbar.make(settingsFileNameEditText, getString(R.string.export_successful), Snackbar.LENGTH_SHORT).show() + Snackbar.make(settingsUriEditText, getString(R.string.export_successful), Snackbar.LENGTH_SHORT).show() else - Snackbar.make(settingsFileNameEditText, getString(R.string.export_failed, passwordEncryptionExportStatus), Snackbar.LENGTH_INDEFINITE).show() + Snackbar.make(settingsUriEditText, getString(R.string.export_failed, passwordEncryptionExportStatus), Snackbar.LENGTH_INDEFINITE).show() } catch (exception: Exception) { // Display a snackbar with the exception. - Snackbar.make(settingsFileNameEditText, getString(R.string.export_failed, exception), Snackbar.LENGTH_INDEFINITE).show() + Snackbar.make(settingsUriEditText, getString(R.string.export_failed, exception), Snackbar.LENGTH_INDEFINITE).show() } } @@ -986,7 +1079,7 @@ class ImportExportActivity : AppCompatActivity() { // Display an export error snackbar if the temporary pre-encrypted export failed. if (openpgpEncryptionExportStatus != EXPORT_SUCCESSFUL) - Snackbar.make(settingsFileNameEditText, getString(R.string.export_failed, openpgpEncryptionExportStatus), Snackbar.LENGTH_INDEFINITE).show() + Snackbar.make(settingsUriEditText, getString(R.string.export_failed, openpgpEncryptionExportStatus), Snackbar.LENGTH_INDEFINITE).show() // Create an encryption intent for OpenKeychain. val openKeychainEncryptIntent = Intent("org.sufficientlysecure.keychain.action.ENCRYPT_DATA") @@ -1004,7 +1097,7 @@ class ImportExportActivity : AppCompatActivity() { openKeychainEncryptActivityResultLauncher.launch(openKeychainEncryptIntent) } catch (exception: Exception) { // Display a snackbar with the exception. - Snackbar.make(settingsFileNameEditText, getString(R.string.export_failed, exception), Snackbar.LENGTH_INDEFINITE).show() + Snackbar.make(settingsUriEditText, getString(R.string.export_failed, exception), Snackbar.LENGTH_INDEFINITE).show() } } } diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt index bbeaef0a..749946b8 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt @@ -5913,7 +5913,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook val dialog = dialogFragment.dialog!! // Get handles for the views. - val fileNameEditText = dialog.findViewById(R.id.file_name_edittext) + val fileNameEditText = dialog.findViewById(R.id.file_uri_edittext) val mhtCheckBox = dialog.findViewById(R.id.mht_checkbox) // Get the file path string. diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkDialog.kt index 7f6bcb3a..dc16f8d9 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkDialog.kt @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-3.0-or-later - * SPDX-FileCopyrightText: 2016-2025 Soren Stoutner + * SPDX-FileCopyrightText: 2016-2026 Soren Stoutner * * This file is part of Privacy Browser Android . * diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.kt index 63e28ef9..d4cf6b26 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.kt @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-3.0-or-later - * SPDX-FileCopyrightText: 2019-2023, 2025 Soren Stoutner + * SPDX-FileCopyrightText: 2019-2023, 2025-2026 Soren Stoutner * * This file is part of Privacy Browser Android . * @@ -25,6 +25,7 @@ import android.content.DialogInterface import android.net.Uri import android.os.Build import android.os.Bundle +import android.provider.OpenableColumns import android.text.Editable import android.text.TextWatcher import android.view.View @@ -36,6 +37,7 @@ import android.widget.TextView import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog +import androidx.core.net.toUri import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment import androidx.preference.PreferenceManager @@ -50,7 +52,7 @@ class OpenDialog : DialogFragment() { private lateinit var openListener: OpenListener // Declare the class views. - private lateinit var fileNameEditText: EditText + private lateinit var fileUriEditText: EditText private lateinit var mhtExplanationTextView: TextView // The public interface is used to send information back to the parent activity. @@ -70,14 +72,14 @@ class OpenDialog : DialogFragment() { private val browseActivityResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { fileUri: Uri? -> // Only do something if the user didn't press back from the file picker. if (fileUri != null) { - // Get the file name string from the URI. - val fileNameString = fileUri.toString() + // Convert the file URI to a string. + val fileUriString = fileUri.toString() - // Set the file name text. - fileNameEditText.setText(fileNameString) + // Populate the file URI edit text. + fileUriEditText.setText(fileUriString) - // Move the cursor to the end of the file name edit text. - fileNameEditText.setSelection(fileNameString.length) + // Move the cursor to the end of the file URI edit text. + fileUriEditText.setSelection(fileUriString.length) } } @@ -121,8 +123,9 @@ class OpenDialog : DialogFragment() { alertDialog.show() // Get handles for the layout items. - fileNameEditText = alertDialog.findViewById(R.id.file_name_edittext)!! + fileUriEditText = alertDialog.findViewById(R.id.file_uri_edittext)!! val browseButton = alertDialog.findViewById