From aa528b46dbf52a0bb4dfa99f549c99e0f8b20ae9 Mon Sep 17 00:00:00 2001 From: Alexander Grahn Date: Thu, 17 Sep 2026 20:48:59 +0200 Subject: [PATCH] refactoring password entry creation and editing when creating a new password entry in the app (i. e. not via autofill), the input field "Name" for the domain is henceforth only mandatory for DirectoryStructure.EncryptedUsername --- CHANGELOG.md | 1 + .../ui/autofill/AutofillSaveActivity.kt | 4 +- .../ui/crypto/PasskeyCreationActivity.kt | 8 +- .../ui/crypto/PasswordCreationActivity.kt | 123 +++++++++++------- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 6 files changed, 86 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d0d40c0e..adec4df93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file ### Fixed - autofill-parser: on some login forms, the "Create entry" button was not shown in the keyboard's suggestions strip +- When adding a new password in the app (i.e., not via the autofill function), the input field "Name" for the domain name will henceforth only be visible if the setting for organising password files is `.../example.org(.gpg)`. ## [2.0.2] - 2026-09-15 diff --git a/app/src/main/java/app/passwordstore/ui/autofill/AutofillSaveActivity.kt b/app/src/main/java/app/passwordstore/ui/autofill/AutofillSaveActivity.kt index 1a39f54cd..dfa73e401 100644 --- a/app/src/main/java/app/passwordstore/ui/autofill/AutofillSaveActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/autofill/AutofillSaveActivity.kt @@ -65,7 +65,7 @@ class AutofillSaveActivity : AppCompatActivity() { * close to existing ones */ val repoPath = repo.absolutePath // io.File -> String - val parentFolderPath = + val destinationFolder = PasswordRepository.findByName(repoPath, origin, PasswordRepository.TYPE_DIR) .firstOrNull() ?.let { @@ -97,7 +97,7 @@ class AutofillSaveActivity : AppCompatActivity() { Bundle().also { it.apply { putBundle(AutofillManager.EXTRA_CLIENT_STATE, clientState) - putString(EXTRA_FOLDER_NAME, parentFolderPath) + putString(EXTRA_FOLDER_NAME, destinationFolder) putString(EXTRA_NAME, origin) putCharArray(EXTRA_ENTRY, encryptedCredentials) putString( diff --git a/app/src/main/java/app/passwordstore/ui/crypto/PasskeyCreationActivity.kt b/app/src/main/java/app/passwordstore/ui/crypto/PasskeyCreationActivity.kt index 47d08034e..871a485f9 100644 --- a/app/src/main/java/app/passwordstore/ui/crypto/PasskeyCreationActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/crypto/PasskeyCreationActivity.kt @@ -584,14 +584,14 @@ class PasskeyCreationActivity : BasePGPActivity() { val path = run { // password item's full file path string val editRelativePath = directory.text.toString().trim() - val passwordDirectory = Paths.get(repoPath, editRelativePath.trim('/')) - passwordDirectory.createDirectories() // ensure destination dir exists - if (!passwordDirectory.exists()) { // should not happen + val destinationFolder = Paths.get(repoPath, editRelativePath.trim('/')) + destinationFolder.createDirectories() // ensure destination dir exists + if (!destinationFolder.exists()) { // should not happen snackbar(message = "Failed to create directory ${editRelativePath.trimEnd('/')}") return@runCatching } - "${passwordDirectory.pathString}/$credentialHexId.gpg" + "${destinationFolder.pathString}/$credentialHexId.gpg" } val passkeyFile = Paths.get(path) diff --git a/app/src/main/java/app/passwordstore/ui/crypto/PasswordCreationActivity.kt b/app/src/main/java/app/passwordstore/ui/crypto/PasswordCreationActivity.kt index 879cc8acc..f41af95c3 100644 --- a/app/src/main/java/app/passwordstore/ui/crypto/PasswordCreationActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/crypto/PasswordCreationActivity.kt @@ -241,20 +241,33 @@ class PasswordCreationActivity : BasePGPActivity() { selectFolderAction.launch(intent) } - if (editing) nameInputLayout.setHint(R.string.crypto_filename_hint) - - if (suggestedName != null) { - name.setText(suggestedName) - } else { - name.requestFocus() - } - val suggestedEntry: PasswordEntry? = suggestedEntryChars?.let { encrypted -> AESEncryption.decrypt(encrypted)?.let { decrypted -> passwordEntryFactory.create(decrypted).also { decrypted.wipe() } } } + /* + * input fields + */ + + // name (domain) when creating, filename when editing + if (suggestedName != null) { + name.setText(suggestedName) + } + + nameInputLayout.visibility = + if ( + suggestedName != null || + AutofillPreferences.directoryStructure(this@PasswordCreationActivity) == + DirectoryStructure.EncryptedUsername + ) + View.VISIBLE + else View.GONE + + if (editing) nameInputLayout.setHint(R.string.crypto_filename_hint) + + // username if (suggestedEntry?.username != null) { val charBuf = CharBuffer.wrap(suggestedEntry?.username) username.setText(charBuf) @@ -275,8 +288,11 @@ class PasswordCreationActivity : BasePGPActivity() { } } } + } else { + usernameInputLayout.visibility = View.VISIBLE } + // password filename if ( !editing && AutofillPreferences.directoryStructure(this@PasswordCreationActivity) == @@ -291,6 +307,8 @@ class PasswordCreationActivity : BasePGPActivity() { charBuf.array()?.wipe() password.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD } + + // extra content suggestedEntry?.extraContentChars?.let { val charBuf = if (it.last() == '\n') CharBuffer.wrap(it.copyOfRange(0, it.size - 1)) @@ -391,16 +409,39 @@ class PasswordCreationActivity : BasePGPActivity() { var editExtra = extraContent.text?.let { CharArray(it.length) { i -> it[i] } } ?: charArrayOf() - if (editName.isBlank()) { - name.requestFocus() - if (editing) snackbar(message = resources.getString(R.string.file_toast_text)) - else snackbar(message = resources.getString(R.string.empty_name_toast_text)) - return@with - } else if (editName.contains('/')) { - name.requestFocus() - if (editing) snackbar(message = resources.getString(R.string.invalid_filename_text)) - else snackbar(message = resources.getString(R.string.invalid_name_text)) - return@with + if ( + editing || + suggestedName != null || + AutofillPreferences.directoryStructure(this@PasswordCreationActivity) == + DirectoryStructure.EncryptedUsername + ) { + if (editName.isBlank()) { + name.requestFocus() + if (editing) snackbar(message = resources.getString(R.string.file_toast_text)) + else snackbar(message = resources.getString(R.string.empty_name_toast_text)) + return@with + } else if (editName.contains('/')) { + name.requestFocus() + if (editing) snackbar(message = resources.getString(R.string.invalid_filename_text)) + else snackbar(message = resources.getString(R.string.invalid_name_text)) + return@with + } + } + + if ( + !editing && + AutofillPreferences.directoryStructure(this@PasswordCreationActivity) != + DirectoryStructure.EncryptedUsername + ) { + if (username.text?.isBlank() ?: true) { + name.requestFocus() + snackbar(message = resources.getString(R.string.empty_username_toast_text)) + return@with + } else if (username.text?.contains('/') ?: false) { + name.requestFocus() + snackbar(message = resources.getString(R.string.invalid_username_text)) + return@with + } } if ( @@ -419,17 +460,6 @@ class PasswordCreationActivity : BasePGPActivity() { } } - if ( - !editing && - AutofillPreferences.directoryStructure(this@PasswordCreationActivity) != - DirectoryStructure.EncryptedUsername && - username.text?.isBlank() ?: true - ) { - username.requestFocus() - snackbar(message = resources.getString(R.string.empty_username_toast_text)) - return@with - } - if (editPass.isEmpty() && editExtra.isEmpty()) { password.requestFocus() snackbar(message = resources.getString(R.string.empty_toast_text)) @@ -460,28 +490,28 @@ class PasswordCreationActivity : BasePGPActivity() { val path = run { // password item's full file path string val editRelativePath = directory.text.toString().trim() - var passwordDirectory = Paths.get(repoPath, editRelativePath.trim('/')) + var destinationFolder = Paths.get(repoPath, editRelativePath.trim('/')) if (!editing) { // fix destination path due to erroneous user input - if (passwordDirectory.endsWith(editName)) { - passwordDirectory = passwordDirectory.parent + if (destinationFolder.endsWith(editName)) { + destinationFolder = destinationFolder.parent } else if ( - passwordDirectory.parent.endsWith(editName) && + destinationFolder.parent.endsWith(editName) && AutofillPreferences.directoryStructure(this@PasswordCreationActivity) == DirectoryStructure.DirectoryBased ) { - passwordDirectory = passwordDirectory.parent.parent + destinationFolder = destinationFolder.parent.parent } when (AutofillPreferences.directoryStructure(this@PasswordCreationActivity)) { DirectoryStructure.FileBased -> { - passwordDirectory = Paths.get(passwordDirectory.pathString, editName) + destinationFolder = Paths.get(destinationFolder.pathString, editName) } DirectoryStructure.DirectoryBased -> { - passwordDirectory = + destinationFolder = Paths.get( - passwordDirectory.pathString, + destinationFolder.pathString, editName, editUsername.concatToString().trim(), ) @@ -491,19 +521,19 @@ class PasswordCreationActivity : BasePGPActivity() { } // ensure destination dir exists - passwordDirectory.createDirectories() - if (!passwordDirectory.exists()) { // should not happen + destinationFolder.createDirectories() + if (!destinationFolder.exists()) { // should not happen snackbar(message = "Failed to create directory ${editRelativePath.trimEnd('/')}") return } - if (editing) "${passwordDirectory.pathString}/$editName.gpg" + if (editing) "${destinationFolder.pathString}/$editName.gpg" else when (AutofillPreferences.directoryStructure(this@PasswordCreationActivity)) { - DirectoryStructure.EncryptedUsername -> "${passwordDirectory.pathString}/$editName.gpg" + DirectoryStructure.EncryptedUsername -> "${destinationFolder.pathString}/$editName.gpg" DirectoryStructure.FileBased -> - "${passwordDirectory.pathString}/${editUsername.concatToString().trim()}.gpg" - DirectoryStructure.DirectoryBased -> "${passwordDirectory.pathString}/$editFilename.gpg" + "${destinationFolder.pathString}/${editUsername.concatToString().trim()}.gpg" + DirectoryStructure.DirectoryBased -> "${destinationFolder.pathString}/$editFilename.gpg" } } @@ -511,9 +541,10 @@ class PasswordCreationActivity : BasePGPActivity() { runCatching { val contentChars = if ( - (AutofillPreferences.directoryStructure(this@PasswordCreationActivity) == - DirectoryStructure.EncryptedUsername || insertUsername.isChecked) && - !editUsername.isEmpty() + !editUsername.isEmpty() && + (editing || + AutofillPreferences.directoryStructure(this@PasswordCreationActivity) == + DirectoryStructure.EncryptedUsername) ) editPass + "\nusername: ".toCharArray() + editUsername + '\n' + editExtra else editPass + '\n' + editExtra diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 225af7712..fe76bcb51 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -353,6 +353,7 @@ .gpg-id wurde gefunden, enthält jedoch eine kurze Hex-ID, die nicht unterstützt wird. Der Dateiname darf kein ‚/‘ enthalten; Verzeichnis oben setzen. Der Name darf kein ‚/‘ enthalten; Verzeichnis oben setzen. + Der Benutzername darf kein ‚/‘ enthalten. Ordner PGP-Schlüssel für Ordner festlegen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d4c949525..61cbae2aa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -356,6 +356,7 @@ Found .gpg-id, but it contains a short hex ID, which is not supported. The file name must not contain ‘/’, set directory above. The name must not contain ‘/’, set directory above. + The username must not contain ‘/’. Directory Set PGP key for directory