diff options
| -rw-r--r-- | .github/workflows/deploy_github_releases.yml | 7 | ||||
| -rw-r--r-- | AGENTS.md | 5 | ||||
| -rw-r--r-- | CHANGELOG.md | 10 | ||||
| -rw-r--r-- | app/src/main/java/app/passwordstore/ui/crypto/CryptoExtensions.kt | 10 | ||||
| -rw-r--r-- | app/src/main/java/app/passwordstore/ui/list/ListDestination.kt | 2 | ||||
| -rw-r--r-- | app/src/main/java/app/passwordstore/ui/list/ListScreen.kt | 11 | ||||
| -rw-r--r-- | app/src/main/java/app/passwordstore/ui/main/MainActivity.kt | 2 | ||||
| -rw-r--r-- | app/src/main/java/app/passwordstore/ui/settings/AboutScreen.kt | 35 | ||||
| -rw-r--r-- | app/src/main/java/app/passwordstore/ui/settings/SettingsGraph.kt | 1 | ||||
| -rw-r--r-- | app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt | 8 | ||||
| -rw-r--r-- | app/src/main/java/app/passwordstore/ui/setup/SshKeyScreen.kt | 2 | ||||
| -rw-r--r-- | app/src/main/res/drawable/ic_open_in_new_24dp.xml | 12 | ||||
| -rw-r--r-- | app/src/main/res/values-it/strings.xml | 1 | ||||
| -rw-r--r-- | app/src/main/res/values/strings.xml | 1 | ||||
| -rw-r--r-- | app/version.properties | 4 |
15 files changed, 85 insertions, 26 deletions
diff --git a/.github/workflows/deploy_github_releases.yml b/.github/workflows/deploy_github_releases.yml index f78687d1..4c0c7bd9 100644 --- a/.github/workflows/deploy_github_releases.yml +++ b/.github/workflows/deploy_github_releases.yml @@ -37,5 +37,8 @@ jobs: apk="HardenedPass_${TAG}.apk" cp app/build/outputs/apk/release/app-release.apk "$apk" sha256sum "$apk" > "$apk.sha256" - gh release create "$TAG" "$apk" "$apk.sha256" --draft --title "$TAG" \ - --notes "See [CHANGELOG.md](https://github.com/${GITHUB_REPOSITORY}/blob/${TAG}/CHANGELOG.md)." + # The release notes are this version's section of the changelog. + awk -v head="## [${TAG#v}]" 'index($0, head) == 1 { found = 1; next } /^## \[/ { found = 0 } found' \ + CHANGELOG.md > notes.md + grep -q '[^[:space:]]' notes.md || { echo "No CHANGELOG.md section for $TAG"; exit 1; } + gh release create "$TAG" "$apk" "$apk.sha256" --draft --title "$TAG" --notes-file notes.md @@ -220,5 +220,8 @@ be updated again. `openssl enc -aes-256-cbc -md sha256 -pbkdf2`; the key is the `ENCRYPT_KEY` repository secret. `scripts/signing-setup.sh` decrypts them, `signing-cleanup.sh` deletes them. - Pushing a `vX.Y.Z` tag runs `deploy_github_releases.yml`: it builds the signed APK and creates - a draft GitHub Release with `HardenedPass_vX.Y.Z.apk` and its `.sha256`. + a draft GitHub Release with `HardenedPass_vX.Y.Z.apk` and its `.sha256`. The notes are that + version's `CHANGELOG.md` section in full, so the release fails without one. +- After publishing, rebuild the website with a push to its `production` branch: its landing page + reads the latest version from GitHub at build time. - Never print, log or commit the keystore password or `ENCRYPT_KEY`. diff --git a/CHANGELOG.md b/CHANGELOG.md index 21f1ce8a..0ec067db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. +## [0.3.2] - 2026-11-05 + +### Changed + +- About moved from Settings to the list's menu, and its links show that they open in the browser + +### Fixed + +- The version, signing certificate and SSH public key are no longer hidden in the clipboard preview + ## [0.3.1] - 2026-11-05 ### Added diff --git a/app/src/main/java/app/passwordstore/ui/crypto/CryptoExtensions.kt b/app/src/main/java/app/passwordstore/ui/crypto/CryptoExtensions.kt index 4a6813f7..626ec130 100644 --- a/app/src/main/java/app/passwordstore/ui/crypto/CryptoExtensions.kt +++ b/app/src/main/java/app/passwordstore/ui/crypto/CryptoExtensions.kt @@ -48,12 +48,14 @@ fun getLongName(fullPath: String, repositoryPath: String, basename: String): Str } } -/** Copies provided [text] to the clipboard, flagged sensitive. */ -fun FragmentActivity.copyTextToClipboard(text: String?) { +/** Copies provided [text] to the clipboard; [sensitive] text is hidden from its preview. */ +fun FragmentActivity.copyTextToClipboard(text: String?, sensitive: Boolean = true) { val clipboard = clipboard ?: return val clip = ClipData.newPlainText("pgp_handler_result_pm", text) - clip.description.extras = - PersistableBundle().apply { putBoolean("android.content.extra.IS_SENSITIVE", true) } + if (sensitive) { + clip.description.extras = + PersistableBundle().apply { putBoolean("android.content.extra.IS_SENSITIVE", true) } + } clipboard.setPrimaryClip(clip) } diff --git a/app/src/main/java/app/passwordstore/ui/list/ListDestination.kt b/app/src/main/java/app/passwordstore/ui/list/ListDestination.kt index 54a7e66c..97092716 100644 --- a/app/src/main/java/app/passwordstore/ui/list/ListDestination.kt +++ b/app/src/main/java/app/passwordstore/ui/list/ListDestination.kt @@ -73,6 +73,7 @@ fun ListDestination( onNewEntry: () -> Unit, onClone: () -> Unit, onSettings: () -> Unit, + onAbout: () -> Unit, onNoStore: () -> Unit, modifier: Modifier = Modifier, viewModel: SearchableRepositoryViewModel = hiltViewModel(), @@ -159,6 +160,7 @@ fun ListDestination( }, onGitOp = { op -> ifInitialized { runGit(op) } }, onSettings = onSettings, + onAbout = onAbout, onNew = { dialog = ListDialog.Create }, onMove = { dialog = ListDialog.Move }, onDelete = { diff --git a/app/src/main/java/app/passwordstore/ui/list/ListScreen.kt b/app/src/main/java/app/passwordstore/ui/list/ListScreen.kt index 1b359d5b..593c744b 100644 --- a/app/src/main/java/app/passwordstore/ui/list/ListScreen.kt +++ b/app/src/main/java/app/passwordstore/ui/list/ListScreen.kt @@ -91,6 +91,7 @@ fun ListScreen( onSync: () -> Unit, onGitOp: (GitOp) -> Unit, onSettings: () -> Unit, + onAbout: () -> Unit, onNew: () -> Unit, onMove: () -> Unit, onDelete: () -> Unit, @@ -133,7 +134,7 @@ fun ListScreen( SelectionBar(rows, selected, onClearSelection, onMove, onDelete, onRename, onPin) } else { Column(horizontalAlignment = Alignment.CenterHorizontally) { - SearchField(query, gitOps, onQueryChange, onGitOp, onSettings, searchFocus) + SearchField(query, gitOps, onQueryChange, onGitOp, onSettings, onAbout, searchFocus) BreadcrumbBar( crumbsFor(path), stringResource(R.string.list_store_root), @@ -238,6 +239,7 @@ private fun SearchField( onQueryChange: (String) -> Unit, onGitOp: (GitOp) -> Unit, onSettings: () -> Unit, + onAbout: () -> Unit, focus: FocusRequester, ) { val focusManager = LocalFocusManager.current @@ -282,6 +284,13 @@ private fun SearchField( onSettings() }, ) + DropdownMenuItem( + text = { Text(stringResource(R.string.about)) }, + onClick = { + menu = false + onAbout() + }, + ) } } } diff --git a/app/src/main/java/app/passwordstore/ui/main/MainActivity.kt b/app/src/main/java/app/passwordstore/ui/main/MainActivity.kt index f3d7d3a0..5037e646 100644 --- a/app/src/main/java/app/passwordstore/ui/main/MainActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/main/MainActivity.kt @@ -30,6 +30,7 @@ import app.passwordstore.ui.git.base.BaseGitActivity import app.passwordstore.ui.list.ListDestination import app.passwordstore.ui.list.folderMoves import app.passwordstore.ui.prompts.PromptHost +import app.passwordstore.ui.settings.AboutRoute import app.passwordstore.ui.settings.SettingsRoute import app.passwordstore.ui.settings.settingsGraph import app.passwordstore.ui.setup.GitServerRoute @@ -130,6 +131,7 @@ class MainActivity : BaseGitActivity() { onSettings = { if (nav.currentBackStackEntry?.id == entry.id) nav.navigate(SettingsRoute) }, + onAbout = { if (nav.currentBackStackEntry?.id == entry.id) nav.navigate(AboutRoute) }, onNoStore = { // The store is gone (deleted from Settings): setup starts over. if (nav.currentBackStackEntry?.id == entry.id) diff --git a/app/src/main/java/app/passwordstore/ui/settings/AboutScreen.kt b/app/src/main/java/app/passwordstore/ui/settings/AboutScreen.kt index fd47c119..4a366390 100644 --- a/app/src/main/java/app/passwordstore/ui/settings/AboutScreen.kt +++ b/app/src/main/java/app/passwordstore/ui/settings/AboutScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -110,7 +111,7 @@ fun AboutDestination( versionLine(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, BuildConfig.GIT_COMMIT), changelog = changelogUrl(BuildConfig.VERSION_NAME), fingerprint = fingerprint, - onCopy = { activity.copyTextToClipboard(it) }, + onCopy = { activity.copyTextToClipboard(it, sensitive = false) }, onOpen = { url -> try { activity.startActivity(Intent(Intent.ACTION_VIEW, url.toUri())) @@ -147,10 +148,10 @@ private fun AboutScreen( Text(stringResource(R.string.about_summary), textAlign = TextAlign.Center) } AboutRow(stringResource(R.string.about_version), version) { onCopy(version) } - AboutRow(stringResource(R.string.about_docs)) { onOpen(DOCUMENTATION) } - AboutRow(stringResource(R.string.about_changelog)) { onOpen(changelog) } - AboutRow(stringResource(R.string.about_source)) { onOpen(REPOSITORY) } - AboutRow(stringResource(R.string.about_issues)) { onOpen("$REPOSITORY/issues") } + LinkRow(stringResource(R.string.about_docs)) { onOpen(DOCUMENTATION) } + LinkRow(stringResource(R.string.about_changelog)) { onOpen(changelog) } + LinkRow(stringResource(R.string.about_source)) { onOpen(REPOSITORY) } + LinkRow(stringResource(R.string.about_issues)) { onOpen("$REPOSITORY/issues") } // White in both themes: the logo's dark outline disappears on a dark background, and Ko-fi // asks that the logo itself stays as it is. Button( @@ -198,11 +199,33 @@ private fun AboutScreen( } @Composable -private fun AboutRow(title: String, summary: String? = null, onClick: () -> Unit) { +private fun AboutRow( + title: String, + summary: String? = null, + trailing: (@Composable () -> Unit)? = null, + onClick: () -> Unit, +) { ListItem( headlineContent = { Text(title) }, supportingContent = summary?.let { { Text(it) } }, + trailingContent = trailing, modifier = Modifier.clickable(role = Role.Button, onClick = onClick), colors = rowColors(), ) } + +/** A row that leaves for the browser, and says so. */ +@Composable +private fun LinkRow(title: String, summary: String? = null, onClick: () -> Unit) { + AboutRow( + title = title, + summary = summary, + trailing = { + Icon( + painterResource(R.drawable.ic_open_in_new_24dp), + contentDescription = stringResource(R.string.about_opens_browser), + ) + }, + onClick = onClick, + ) +} diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsGraph.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsGraph.kt index 44ae06d8..ba4d158c 100644 --- a/app/src/main/java/app/passwordstore/ui/settings/SettingsGraph.kt +++ b/app/src/main/java/app/passwordstore/ui/settings/SettingsGraph.kt @@ -47,7 +47,6 @@ fun NavGraphBuilder.settingsGraph(activity: MainActivity, nav: NavHostController composable<SettingsRoute> { entry -> SettingsScreen( onSection = { if (onTop(entry)) nav.navigate(SettingsSectionRoute(it)) }, - onAbout = { if (onTop(entry)) nav.navigate(AboutRoute) }, onBack = { close(entry) }, ) } diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt index 14895017..ed78608f 100644 --- a/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt @@ -17,7 +17,6 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material.icons.filled.Info import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -98,7 +97,6 @@ internal fun SettingsScaffold( @Composable fun SettingsScreen( onSection: (SettingsSection) -> Unit, - onAbout: () -> Unit, onBack: () -> Unit, modifier: Modifier = Modifier, ) { @@ -111,12 +109,6 @@ fun SettingsScreen( colors = rowColors(), ) } - ListItem( - headlineContent = { Text(stringResource(R.string.about)) }, - modifier = Modifier.clickable(role = Role.Button, onClick = onAbout), - leadingContent = { Icon(Icons.Filled.Info, contentDescription = null) }, - colors = rowColors(), - ) } } diff --git a/app/src/main/java/app/passwordstore/ui/setup/SshKeyScreen.kt b/app/src/main/java/app/passwordstore/ui/setup/SshKeyScreen.kt index f93fc9be..390f1160 100644 --- a/app/src/main/java/app/passwordstore/ui/setup/SshKeyScreen.kt +++ b/app/src/main/java/app/passwordstore/ui/setup/SshKeyScreen.kt @@ -195,7 +195,7 @@ fun SshKeyDestination( busy = viewModel.busy, onImport = { if (SshKey.exists) replace = true else picker.launch(arrayOf("*/*")) }, onGenerate = onGenerate, - onCopy = { publicKey?.let { activity.copyTextToClipboard(it) } }, + onCopy = { publicKey?.let { activity.copyTextToClipboard(it, sensitive = false) } }, onShare = { publicKey?.let { activity.startActivity(shareKey(it)) } }, onBack = { if (!viewModel.busy) onClose() }, ) diff --git a/app/src/main/res/drawable/ic_open_in_new_24dp.xml b/app/src/main/res/drawable/ic_open_in_new_24dp.xml new file mode 100644 index 00000000..b82ebe62 --- /dev/null +++ b/app/src/main/res/drawable/ic_open_in_new_24dp.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:autoMirrored="true" + android:tint="?android:attr/colorControlNormal" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="@android:color/white" + android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z" /> +</vector> diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index f6de40bd..07f63cd9 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -393,5 +393,6 @@ <string name="about_license">Licenza</string> <string name="about_license_text">GNU General Public License v3.0 only. Copyright degli autori di Android Password Store e dei contributori di HardenedPass.</string> <string name="about_libraries">Licenze open source</string> + <string name="about_opens_browser">Si apre nel browser</string> <string name="about_no_browser">Nessun browser trovato per aprire questo link</string> </resources> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 38ef2cdc..c8782fad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -427,5 +427,6 @@ <string name="about_license">License</string> <string name="about_license_text">GNU General Public License v3.0 only. Copyright the Android Password Store authors and HardenedPass contributors.</string> <string name="about_libraries">Open source licenses</string> + <string name="about_opens_browser">Opens in the browser</string> <string name="about_no_browser">No browser found to open this link</string> </resources> diff --git a/app/version.properties b/app/version.properties index 04fc45ad..e6c7d4f4 100644 --- a/app/version.properties +++ b/app/version.properties @@ -1,5 +1,5 @@ # # This file was automatically generated by 'versioning-plugin'. DO NOT EDIT MANUALLY. # -versioning-plugin.versionCode=301 -versioning-plugin.versionName=0.3.1 +versioning-plugin.versionCode=302 +versioning-plugin.versionName=0.3.2 |
