aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-11-03 18:36:42 +0100
committerDanilo M. <danix@danix.xyz>2026-11-03 18:36:42 +0100
commitcc880cf62ea8391f693f034858578cd4b693043b (patch)
tree767a1ebb24b6c4e94bd0c3073a2cb5e98d406c0a /app/src/main/java
parent9dba2efd0b8d9d117956d671468361acac060482 (diff)
downloadHardenedPass-cc880cf62ea8391f693f034858578cd4b693043b.tar.gz
HardenedPass-cc880cf62ea8391f693f034858578cd4b693043b.zip
feat(app): drop two sort orders, fix the proxy user name
The Compose list shows folders and entries in separate groups, so the files first and type independent sort orders did nothing visible. They are removed; a stored one migrates to folders first, and any unknown value reads as folders first instead of throwing. The OpenKeychain SSH key ID, which nothing reads any more, is removed once. GitSettings wrote the proxy user name to the proxy preferences but read it from the main settings, so ProxyUtils never authenticated. Adds the pure parts of the settings port: proxy port parsing, HEAD status and the commit log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'app/src/main/java')
-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
5 files changed, 106 insertions, 18 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(