Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -275,8 +288,11 @@ class PasswordCreationActivity : BasePGPActivity() {
}
}
}
} else {
usernameInputLayout.visibility = View.VISIBLE
}

// password filename
if (
!editing &&
AutofillPreferences.directoryStructure(this@PasswordCreationActivity) ==
Expand All @@ -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))
Expand Down Expand Up @@ -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 (
Expand All @@ -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))
Expand Down Expand Up @@ -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(),
)
Expand All @@ -491,29 +521,30 @@ 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"
}
}

lifecycleScope.launch(dispatcherProvider.main()) {
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
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@
<string name="short_gpg_id">.gpg-id wurde gefunden, enthält jedoch eine kurze Hex-ID, die nicht unterstützt wird.</string>
<string name="invalid_filename_text">Der Dateiname darf kein ‚/‘ enthalten; Verzeichnis oben setzen.</string>
<string name="invalid_name_text">Der Name darf kein ‚/‘ enthalten; Verzeichnis oben setzen.</string>
<string name="invalid_username_text">Der Benutzername darf kein ‚/‘ enthalten.</string>
<string name="directory_hint">Ordner</string>
<string name="new_folder_set_gpg_key">PGP-Schlüssel für Ordner festlegen</string>

Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@
<string name="short_gpg_id">Found .gpg-id, but it contains a short hex ID, which is not supported.</string>
<string name="invalid_filename_text">The file name must not contain ‘/’, set directory above.</string>
<string name="invalid_name_text">The name must not contain ‘/’, set directory above.</string>
<string name="invalid_username_text">The username must not contain ‘/’.</string>
<string name="directory_hint">Directory</string>
<string name="new_folder_set_gpg_key">Set PGP key for directory</string>

Expand Down