aboutsummaryrefslogtreecommitdiffstats
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/java/app/passwordstore/ui/settings/SettingsModel.kt79
-rw-r--r--app/src/main/java/app/passwordstore/util/settings/GitSettings.kt2
-rw-r--r--app/src/main/java/app/passwordstore/util/settings/Migrations.kt19
-rw-r--r--app/src/main/java/app/passwordstore/util/settings/PasswordSortOrder.kt17
-rw-r--r--app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt7
-rw-r--r--app/src/main/res/values-de/strings.xml2
-rw-r--r--app/src/main/res/values-fr/strings.xml2
-rw-r--r--app/src/main/res/values-gl/strings.xml2
-rw-r--r--app/src/main/res/values-it/strings.xml2
-rw-r--r--app/src/main/res/values-ko-rKR/strings.xml2
-rw-r--r--app/src/main/res/values-pl-rPL/strings.xml2
-rw-r--r--app/src/main/res/values-pt-rBR/strings.xml2
-rw-r--r--app/src/main/res/values-ru/strings.xml2
-rw-r--r--app/src/main/res/values-zh-rCN/strings.xml2
-rw-r--r--app/src/main/res/values/arrays.xml4
-rw-r--r--app/src/main/res/values/strings.xml2
-rw-r--r--app/src/test/java/app/passwordstore/ui/settings/SettingsModelTest.kt90
-rw-r--r--app/src/test/java/app/passwordstore/util/settings/GitSettingsTest.kt27
-rw-r--r--app/src/test/java/app/passwordstore/util/settings/MigrationsTest.kt25
-rw-r--r--app/src/test/java/app/passwordstore/util/settings/PasswordSortOrderTest.kt26
20 files changed, 274 insertions, 42 deletions
diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsModel.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsModel.kt
new file mode 100644
index 00000000..eca6770c
--- /dev/null
+++ b/app/src/main/java/app/passwordstore/ui/settings/SettingsModel.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package app.passwordstore.ui.settings
+
+import app.passwordstore.util.extensions.hash
+import app.passwordstore.util.extensions.time
+import app.passwordstore.util.git.GitCommit
+import java.io.IOException
+import logcat.LogPriority.ERROR
+import logcat.asLog
+import logcat.logcat
+import org.eclipse.jgit.api.Git
+import org.eclipse.jgit.api.errors.GitAPIException
+import org.eclipse.jgit.api.errors.JGitInternalException
+import org.eclipse.jgit.lib.Constants
+import org.eclipse.jgit.lib.Repository
+import org.eclipse.jgit.lib.RepositoryState
+
+private const val TAG = "SettingsModel"
+
+/**
+ * The proxy port typed as [text]: -1 when empty (no port, as stored before any was set), null when
+ * it is not a port number from 1 to 65535.
+ */
+fun proxyPort(text: String): Int? {
+ val trimmed = text.trim()
+ if (trimmed.isEmpty()) return -1
+ return trimmed.toIntOrNull()?.takeIf { it in 1..65535 }
+}
+
+/** Where HEAD points, as the git config screen reports it. */
+sealed interface HeadStatus {
+ data class Branch(val name: String) : HeadStatus
+
+ data class Detached(val hash: String) : HeadStatus
+
+ data object Missing : HeadStatus
+}
+
+/** [repo]'s HEAD: a branch (also one without commits yet), a detached commit, or missing. */
+fun headStatus(repo: Repository): HeadStatus =
+ try {
+ val head = repo.findRef(Constants.HEAD)
+ when {
+ head == null -> HeadStatus.Missing
+ head.isSymbolic -> HeadStatus.Branch(Repository.shortenRefName(head.target.name))
+ else ->
+ head.objectId?.let { HeadStatus.Detached(it.abbreviate(8).name()) } ?: HeadStatus.Missing
+ }
+ } catch (e: IOException) {
+ logcat(TAG, ERROR) { e.asLog() }
+ HeadStatus.Missing
+ }
+
+/** Whether a rebase or a merge is in progress, so "Abort rebase" has something to abort. */
+fun needsAbort(repo: Repository): Boolean =
+ repo.repositoryState.let { it.isRebasing || it == RepositoryState.MERGING }
+
+/**
+ * The commits reachable from HEAD, newest first; empty without a repository or a commit. Blocking:
+ * call it off the main thread.
+ */
+// ponytail: the whole log at once, as the old screen did (tested there with 1000 commits); page it
+// if a store's history ever makes this slow.
+fun gitLog(repo: Repository?): List<GitCommit> {
+ if (repo == null) return emptyList()
+ return try {
+ Git(repo).log().call().map { GitCommit(it.hash, it.shortMessage, it.authorIdent.name, it.time) }
+ } catch (e: GitAPIException) {
+ logcat(TAG, ERROR) { e.asLog() }
+ emptyList()
+ } catch (e: JGitInternalException) {
+ logcat(TAG, ERROR) { e.asLog() }
+ emptyList()
+ }
+}
diff --git a/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt b/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt
index d315daf3..004028c4 100644
--- a/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt
+++ b/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt
@@ -108,7 +108,7 @@ constructor(
}
var proxyUsername
- get() = settings.getString(PreferenceKeys.PROXY_USERNAME)
+ get() = proxySettings.getString(PreferenceKeys.PROXY_USERNAME)
set(value) {
proxySettings.edit { putString(PreferenceKeys.PROXY_USERNAME, value) }
}
diff --git a/app/src/main/java/app/passwordstore/util/settings/Migrations.kt b/app/src/main/java/app/passwordstore/util/settings/Migrations.kt
index 06075655..82847f02 100644
--- a/app/src/main/java/app/passwordstore/util/settings/Migrations.kt
+++ b/app/src/main/java/app/passwordstore/util/settings/Migrations.kt
@@ -32,6 +32,8 @@ fun runMigrations(filesDirPath: String, sharedPrefs: SharedPreferences, gitSetti
migrateToDiceware(sharedPrefs)
removeExternalStorageProperties(sharedPrefs)
removeCurrentBranchValue(sharedPrefs)
+ migrateRemovedSortOrders(sharedPrefs)
+ removeOpenKeystoreKeyId(sharedPrefs)
}
/**
@@ -194,3 +196,20 @@ private fun removeExternalStorageProperties(prefs: SharedPreferences) {
}
}
}
+
+/**
+ * "Files first" and "type independent" were removed with the Compose list, which shows folders and
+ * entries in separate groups: a stored one becomes folders first, the list's default.
+ */
+private fun migrateRemovedSortOrders(sharedPrefs: SharedPreferences) {
+ val stored = sharedPrefs.getString(PreferenceKeys.SORT_ORDER) ?: return
+ if (stored != "FILE_FIRST" && stored != "INDEPENDENT") return
+ logcat(TAG, INFO) { "Replacing the removed sort order $stored" }
+ sharedPrefs.edit { putString(PreferenceKeys.SORT_ORDER, PasswordSortOrder.FOLDER_FIRST.name) }
+}
+
+/** OpenKeychain SSH support is gone and nothing reads the remembered key ID any more. */
+private fun removeOpenKeystoreKeyId(sharedPrefs: SharedPreferences) {
+ if (!sharedPrefs.contains(PreferenceKeys.SSH_OPENKEYSTORE_KEYID)) return
+ sharedPrefs.edit { remove(PreferenceKeys.SSH_OPENKEYSTORE_KEYID) }
+}
diff --git a/app/src/main/java/app/passwordstore/util/settings/PasswordSortOrder.kt b/app/src/main/java/app/passwordstore/util/settings/PasswordSortOrder.kt
index 224d9d66..921e9bc4 100644
--- a/app/src/main/java/app/passwordstore/util/settings/PasswordSortOrder.kt
+++ b/app/src/main/java/app/passwordstore/util/settings/PasswordSortOrder.kt
@@ -18,11 +18,6 @@ enum class PasswordSortOrder(val comparator: java.util.Comparator<PasswordItem>)
(p1.type + p1.name).compareTo(p2.type + p2.name, ignoreCase = true)
}
),
- INDEPENDENT(
- Comparator { p1: PasswordItem, p2: PasswordItem ->
- p1.name.compareTo(p2.name, ignoreCase = true)
- }
- ),
RECENTLY_USED(
Comparator { p1: PasswordItem, p2: PasswordItem ->
val recentHistory =
@@ -36,18 +31,18 @@ enum class PasswordSortOrder(val comparator: java.util.Comparator<PasswordItem>)
else -> p1.name.compareTo(p2.name, ignoreCase = true)
}
}
- ),
- FILE_FIRST(
- Comparator { p1: PasswordItem, p2: PasswordItem ->
- (p2.type + p1.name).compareTo(p1.type + p2.name, ignoreCase = true)
- }
);
companion object {
+ /**
+ * The stored sort order; anything else (the removed "files first" and "type independent", a
+ * value from another version) reads as [FOLDER_FIRST].
+ */
@JvmStatic
fun getSortOrder(settings: SharedPreferences): PasswordSortOrder {
- return valueOf(settings.getString(PreferenceKeys.SORT_ORDER) ?: FOLDER_FIRST.name)
+ val stored = settings.getString(PreferenceKeys.SORT_ORDER)
+ return entries.firstOrNull { it.name == stored } ?: FOLDER_FIRST
}
}
}
diff --git a/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt b/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt
index ca570dc4..f8cbf1e4 100644
--- a/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt
+++ b/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt
@@ -71,12 +71,7 @@ private fun PasswordItem.Companion.makeComparator(
directoryStructure: DirectoryStructure,
): Comparator<PasswordItem> {
return when (typeSortOrder) {
- PasswordSortOrder.FOLDER_FIRST -> compareBy { it.type }
- // In order to let INDEPENDENT not distinguish between items based on their type, we
- // simply
- // declare them all equal at this stage.
- PasswordSortOrder.INDEPENDENT -> Comparator { _, _ -> 0 }
- PasswordSortOrder.FILE_FIRST -> compareByDescending { it.type }
+ PasswordSortOrder.FOLDER_FIRST -> compareBy<PasswordItem> { it.type }
PasswordSortOrder.RECENTLY_USED -> PasswordSortOrder.RECENTLY_USED.comparator
}
.then(
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 493391a8..618aac0d 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -96,8 +96,6 @@
<string name="pref_recursive_filter_summary">Findet Passwörter auch in Unterordnern.</string>
<string name="pref_sort_order_title">Passwortsortierung</string>
<string name="pref_folder_first_sort_order">Ordner zuerst</string>
- <string name="pref_file_first_sort_order">Dateien zuerst</string>
- <string name="pref_type_independent_sort_order">typunabhängig</string>
<string name="pref_recently_used_sort_order">Zuletzt verwendet</string>
<string name="pref_category_autofill_title">Automatisch ausfüllen</string>
<string name="pref_autofill_enable_title">Autofill aktivieren</string>
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index d2a469e7..4cc7d29d 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -94,8 +94,6 @@
<string name="pref_recursive_filter_summary">Cherche le mot de passe dans tous les sous-répertoires du répertoire actuel.</string>
<string name="pref_sort_order_title">Ordre de tri des mots de passe</string>
<string name="pref_folder_first_sort_order">Dossiers en premier</string>
- <string name="pref_file_first_sort_order">Fichiers en premier</string>
- <string name="pref_type_independent_sort_order">Indifférent au type d\'entrée</string>
<string name="pref_recently_used_sort_order">Récemment utilisé</string>
<string name="pref_category_autofill_title">Saisie automatique</string>
<string name="pref_autofill_enable_title">Saisie automatique</string>
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index ad4c663d..34b1ce6a 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -97,8 +97,6 @@
<string name="pref_directory_structure">Organización dos contrasinais</string>
<string name="pref_sort_order_title">Orde para mostrar contrasinais</string>
<string name="pref_folder_first_sort_order">Primeiro cartafoles</string>
- <string name="pref_file_first_sort_order">Primeiro ficheiros</string>
- <string name="pref_type_independent_sort_order">Independentemente do tipo</string>
<string name="pref_recently_used_sort_order">Usadas recentemente</string>
<string name="pref_category_autofill_title">Completado automático</string>
<string name="pref_autofill_enable_title">Activar completado automático</string>
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 8edc9d3d..9b67482c 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -94,8 +94,6 @@
<string name="pref_recursive_filter_summary">Trova ricorsivamente le password della directory corrente.</string>
<string name="pref_sort_order_title">Ordine password</string>
<string name="pref_folder_first_sort_order">Prima le cartelle</string>
- <string name="pref_file_first_sort_order">Prima i file</string>
- <string name="pref_type_independent_sort_order">Indipendente dal tipo</string>
<string name="pref_recently_used_sort_order">Usato di recente</string>
<string name="pref_category_autofill_title">Compilazione automatica</string>
<string name="pref_autofill_enable_title">Attiva la compilazione automatica</string>
diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml
index aaa438dd..06e0a555 100644
--- a/app/src/main/res/values-ko-rKR/strings.xml
+++ b/app/src/main/res/values-ko-rKR/strings.xml
@@ -95,8 +95,6 @@
<string name="pref_directory_structure">암허 파일 정리</string>
<string name="pref_sort_order_title">암호 정렬 순서</string>
<string name="pref_folder_first_sort_order">폴더 우선</string>
- <string name="pref_file_first_sort_order">파일 우선</string>
- <string name="pref_type_independent_sort_order">타입순</string>
<string name="pref_recently_used_sort_order">최신순</string>
<string name="pref_category_autofill_title">자동완성</string>
<string name="pref_autofill_enable_title">자동완성 활성화</string>
diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml
index 815cf41c..6bdb966c 100644
--- a/app/src/main/res/values-pl-rPL/strings.xml
+++ b/app/src/main/res/values-pl-rPL/strings.xml
@@ -94,8 +94,6 @@
<string name="pref_directory_structure">Organizacja plików haseł</string>
<string name="pref_sort_order_title">Kolejność sortowania haseł</string>
<string name="pref_folder_first_sort_order">Najpierw foldery</string>
- <string name="pref_file_first_sort_order">Najpierw pliki</string>
- <string name="pref_type_independent_sort_order">Niezależnie od typu</string>
<string name="pref_recently_used_sort_order">Ostatnio używane</string>
<string name="pref_category_autofill_title">Autouzupełnianie</string>
<string name="pref_autofill_enable_title">Włącz autouzupełnianie</string>
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index a1d1a38c..8f40c831 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -96,8 +96,6 @@
<string name="pref_recursive_filter_summary">Encontrar senhas do diretório corrente recursivamente.</string>
<string name="pref_sort_order_title">Ordenação da Senha</string>
<string name="pref_folder_first_sort_order">Pastas primeiro</string>
- <string name="pref_file_first_sort_order">Arquivos primeiro</string>
- <string name="pref_type_independent_sort_order">Tipo independente</string>
<string name="pref_recently_used_sort_order">Usado recentemente</string>
<string name="pref_category_autofill_title">Preenchimento Automático</string>
<string name="pref_autofill_enable_title">Ativar preenchimento automático</string>
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index d0934a4d..af3078bc 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -99,8 +99,6 @@
<string name="pref_recursive_filter_summary">Рекурсивный поиск паролей в текущей директории</string>
<string name="pref_sort_order_title">Порядок сортировки паролей</string>
<string name="pref_folder_first_sort_order">Сначала папки</string>
- <string name="pref_file_first_sort_order">Сначала файлы</string>
- <string name="pref_type_independent_sort_order">Типонезависимый</string>
<string name="pref_recently_used_sort_order">Недавно использованные</string>
<string name="pref_category_autofill_title">Автозаполнение</string>
<string name="pref_autofill_enable_title">Включить автозаполнение</string>
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index ddcdda1a..959d5fac 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -95,8 +95,6 @@
<string name="pref_recursive_filter_summary">递归式寻找当前目录的密码</string>
<string name="pref_sort_order_title">密码排序顺序</string>
<string name="pref_folder_first_sort_order">目录优先</string>
- <string name="pref_file_first_sort_order">文件优先</string>
- <string name="pref_type_independent_sort_order">类型无关</string>
<string name="pref_recently_used_sort_order">最近使用</string>
<string name="pref_category_autofill_title">自动填充</string>
<string name="pref_autofill_enable_title">启用自动填充</string>
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 9f44710b..2891987d 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -6,14 +6,10 @@
<resources>
<string-array name="sort_order_entries">
<item>@string/pref_folder_first_sort_order</item>
- <item>@string/pref_file_first_sort_order</item>
- <item>@string/pref_type_independent_sort_order</item>
<item>@string/pref_recently_used_sort_order</item>
</string-array>
<string-array name="sort_order_values">
<item>FOLDER_FIRST</item>
- <item>FILE_FIRST</item>
- <item>INDEPENDENT</item>
<item>RECENTLY_USED</item>
</string-array>
<string-array name="pwgen_provider_labels">
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 59ba74cb..dc9140f8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -116,8 +116,6 @@
<string name="pref_directory_structure">Password file organization</string>
<string name="pref_sort_order_title">Password sort order</string>
<string name="pref_folder_first_sort_order">Folders first</string>
- <string name="pref_file_first_sort_order">Files first</string>
- <string name="pref_type_independent_sort_order">Type independent</string>
<string name="pref_recently_used_sort_order">Recently used</string>
<string name="pref_category_autofill_title">Autofill</string>
<string name="pref_autofill_enable_title">Enable Autofill</string>
diff --git a/app/src/test/java/app/passwordstore/ui/settings/SettingsModelTest.kt b/app/src/test/java/app/passwordstore/ui/settings/SettingsModelTest.kt
new file mode 100644
index 00000000..693e07dd
--- /dev/null
+++ b/app/src/test/java/app/passwordstore/ui/settings/SettingsModelTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package app.passwordstore.ui.settings
+
+import app.passwordstore.util.git.GitCommit
+import java.io.File
+import java.nio.file.Files
+import kotlin.test.AfterTest
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertNull
+import org.eclipse.jgit.api.Git
+import org.eclipse.jgit.lib.PersonIdent
+import org.eclipse.jgit.revwalk.RevCommit
+import org.eclipse.jgit.util.FS
+
+class SettingsModelTest {
+
+ private val root = Files.createTempDirectory("aps-settings").toFile()
+ private val identity = PersonIdent("test", "test@example.org")
+
+ init {
+ // Ignore the developer's ~/.gitconfig (default branch, commit signing).
+ FS.DETECTED.setUserHome(root)
+ }
+
+ @AfterTest fun cleanUp() = root.deleteRecursively().let {}
+
+ private fun repo(): Git =
+ Git.init().setInitialBranch("master").setDirectory(File(root, "store")).call()
+
+ private fun Git.commitFile(name: String, message: String): RevCommit {
+ File(repository.workTree, name).writeText(message)
+ add().addFilepattern(name).call()
+ return commit()
+ .setMessage(message)
+ .setAuthor(identity)
+ .setCommitter(identity)
+ .setSign(false)
+ .call()
+ }
+
+ @Test
+ fun proxyPorts() {
+ assertEquals(-1, proxyPort(""))
+ assertEquals(-1, proxyPort(" "))
+ assertEquals(8080, proxyPort("8080"))
+ assertEquals(8080, proxyPort(" 8080 "))
+ assertEquals(1, proxyPort("1"))
+ assertEquals(65535, proxyPort("65535"))
+ assertNull(proxyPort("0"))
+ assertNull(proxyPort("65536"))
+ assertNull(proxyPort("99999999999"))
+ assertNull(proxyPort("-1"))
+ assertNull(proxyPort("80a"))
+ }
+
+ @Test
+ fun headStatusFollowsHead() {
+ repo().use { git ->
+ // A new repository: HEAD names a branch that has no commit yet.
+ assertEquals(HeadStatus.Branch("master"), headStatus(git.repository))
+ val first = git.commitFile("a.gpg", "Add a")
+ git.commitFile("b.gpg", "Add b")
+ assertEquals(HeadStatus.Branch("master"), headStatus(git.repository))
+ assertFalse(needsAbort(git.repository))
+ git.checkout().setName(first.name).call()
+ assertEquals(HeadStatus.Detached(first.name.take(8)), headStatus(git.repository))
+ }
+ }
+
+ @Test
+ fun gitLogIsNewestFirst() {
+ assertEquals(emptyList<GitCommit>(), gitLog(null))
+ repo().use { git ->
+ // No commit yet: JGit's NoHeadException is an empty log, not a crash.
+ assertEquals(emptyList<GitCommit>(), gitLog(git.repository))
+ val first = git.commitFile("a.gpg", "Add a")
+ val second = git.commitFile("b.gpg", "Add b")
+ val log = gitLog(git.repository)
+ assertEquals(listOf("Add b", "Add a"), log.map(GitCommit::shortMessage))
+ assertEquals(listOf(second.name, first.name), log.map(GitCommit::hash))
+ assertEquals("test", log[0].authorName)
+ }
+ }
+}
diff --git a/app/src/test/java/app/passwordstore/util/settings/GitSettingsTest.kt b/app/src/test/java/app/passwordstore/util/settings/GitSettingsTest.kt
new file mode 100644
index 00000000..6b30c248
--- /dev/null
+++ b/app/src/test/java/app/passwordstore/util/settings/GitSettingsTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package app.passwordstore.util.settings
+
+import app.passwordstore.util.extensions.getString
+import com.github.ivanshafran.sharedpreferencesmock.SPMockBuilder
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
+
+class GitSettingsTest {
+
+ @Test
+ fun proxyUsernameIsReadWhereItIsWritten() {
+ val settings = SPMockBuilder().createSharedPreferences()
+ val proxy = SPMockBuilder().createSharedPreferences()
+ val gitSettings =
+ GitSettings(settings, SPMockBuilder().createSharedPreferences(), proxy, "/nonexistent")
+ gitSettings.proxyUsername = "proxy-user"
+ assertEquals("proxy-user", gitSettings.proxyUsername)
+ assertEquals("proxy-user", proxy.getString(PreferenceKeys.PROXY_USERNAME))
+ assertNull(settings.getString(PreferenceKeys.PROXY_USERNAME))
+ }
+}
diff --git a/app/src/test/java/app/passwordstore/util/settings/MigrationsTest.kt b/app/src/test/java/app/passwordstore/util/settings/MigrationsTest.kt
index 62109563..44bd12ba 100644
--- a/app/src/test/java/app/passwordstore/util/settings/MigrationsTest.kt
+++ b/app/src/test/java/app/passwordstore/util/settings/MigrationsTest.kt
@@ -184,4 +184,29 @@ class MigrationsTest {
assertFalse { sharedPrefs.contains(PreferenceKeys.GIT_EXTERNAL_REPO) }
assertTrue { sharedPrefs.getBoolean(PreferenceKeys.GIT_EXTERNAL_MIGRATED, false) }
}
+
+ @Test
+ fun removedSortOrdersFallBackToFoldersFirst() {
+ val gitSettings =
+ GitSettings(sharedPrefs, encryptedSharedPreferences, proxySharedPreferences, filesDir)
+ for (removed in listOf("FILE_FIRST", "INDEPENDENT")) {
+ sharedPrefs.edit { putString(PreferenceKeys.SORT_ORDER, removed) }
+ runMigrations(filesDir, sharedPrefs, gitSettings)
+ assertEquals("FOLDER_FIRST", sharedPrefs.getString(PreferenceKeys.SORT_ORDER))
+ }
+ sharedPrefs.edit { putString(PreferenceKeys.SORT_ORDER, "RECENTLY_USED") }
+ runMigrations(filesDir, sharedPrefs, gitSettings)
+ assertEquals("RECENTLY_USED", sharedPrefs.getString(PreferenceKeys.SORT_ORDER))
+ }
+
+ @Test
+ fun staleOpenKeystoreKeyIdIsRemoved() {
+ sharedPrefs.edit { putString(PreferenceKeys.SSH_OPENKEYSTORE_KEYID, "0123456789ABCDEF") }
+ runMigrations(
+ filesDir,
+ sharedPrefs,
+ GitSettings(sharedPrefs, encryptedSharedPreferences, proxySharedPreferences, filesDir),
+ )
+ assertFalse(sharedPrefs.contains(PreferenceKeys.SSH_OPENKEYSTORE_KEYID))
+ }
}
diff --git a/app/src/test/java/app/passwordstore/util/settings/PasswordSortOrderTest.kt b/app/src/test/java/app/passwordstore/util/settings/PasswordSortOrderTest.kt
new file mode 100644
index 00000000..34348692
--- /dev/null
+++ b/app/src/test/java/app/passwordstore/util/settings/PasswordSortOrderTest.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package app.passwordstore.util.settings
+
+import androidx.core.content.edit
+import com.github.ivanshafran.sharedpreferencesmock.SPMockBuilder
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class PasswordSortOrderTest {
+
+ @Test
+ fun unknownAndRemovedValuesReadAsFoldersFirst() {
+ val prefs = SPMockBuilder().createSharedPreferences()
+ assertEquals(PasswordSortOrder.FOLDER_FIRST, PasswordSortOrder.getSortOrder(prefs))
+ for (value in listOf("FILE_FIRST", "INDEPENDENT", "bogus")) {
+ prefs.edit { putString(PreferenceKeys.SORT_ORDER, value) }
+ assertEquals(PasswordSortOrder.FOLDER_FIRST, PasswordSortOrder.getSortOrder(prefs))
+ }
+ prefs.edit { putString(PreferenceKeys.SORT_ORDER, "RECENTLY_USED") }
+ assertEquals(PasswordSortOrder.RECENTLY_USED, PasswordSortOrder.getSortOrder(prefs))
+ }
+}