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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## Unreleased

- The check code stays on screen once enrolment finishes, labelled and with a reminder that it
should match the board and Sapwood, instead of disappearing as soon as the board's answer is
confirmed. It clears when the owner leaves the screen.
- Release builds no longer embed git metadata, so the APK built by F-Droid matches the one
published here whatever checkout it was built from.

## 0.7.0 (2026-09-26)

- Adding a phone now scans Sapwood's own invite QR instead of the other way round: "Add a phone"
Expand Down
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ android {
buildTypes {
release {
isMinifyEnabled = false
// Keep git metadata out of the APK: a build from a worktree or a tarball records
// NO_VALID_GIT_FOUND where F-Droid's clone records the commit, and that one file was
// enough to fail F-Droid's reproducible-build check for 0.5.0.
vcsInfo.include = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
if (releaseSigning.getProperty("storeFile") != null) {
signingConfig = signingConfigs.getByName("release")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ class UnlockEnrolActivity : AppCompatActivity() {
conflicted = true
binding.enrolBatteryButton.isVisible = false
binding.enrolStatus.text = getString(R.string.enrol_conflict_after, storedId, handOff.id)
binding.enrolCheckCode.isVisible = false
showCheckCode(false)
}
handOff.wipe()
return
Expand All @@ -300,7 +300,7 @@ class UnlockEnrolActivity : AppCompatActivity() {
enrolSecretHex = null
binding.enrolConfirmButton.isVisible = false
binding.enrolStatus.text = getString(R.string.enrol_conflict, first.id, handOff.id)
binding.enrolCheckCode.isVisible = false
showCheckCode(false)
}
handOff.wipe()
return
Expand All @@ -319,7 +319,7 @@ class UnlockEnrolActivity : AppCompatActivity() {
binding.enrolRetryButton.isVisible = false
binding.enrolStatus.text = getString(R.string.enrol_received)
binding.enrolCheckCode.text = checkCode(envelope.ephemeralPubkeyHex).orEmpty()
binding.enrolCheckCode.isVisible = true
showCheckCode(true)
binding.enrolConfirmButton.isVisible = true
lifecycleScope.launch {
delay(PENDING_TIMEOUT_MILLIS)
Expand All @@ -330,7 +330,7 @@ class UnlockEnrolActivity : AppCompatActivity() {
enrolSecretHex = null
binding.enrolConfirmButton.isVisible = false
binding.enrolStatus.text = getString(R.string.enrol_timed_out, handOff.id)
binding.enrolCheckCode.isVisible = false
showCheckCode(false)
}
}
promptSeal()
Expand Down Expand Up @@ -393,8 +393,9 @@ class UnlockEnrolActivity : AppCompatActivity() {
PairingStore(this).setKeepAliveEnabled(true)
HeartwoodKeepAliveService.start(this)

// Left showing on Done: the owner may still be comparing it with Sapwood or the board's
// own card at this point, and it stays harmless to see once the slot secret is sealed.
binding.enrolConfirmButton.isVisible = false
binding.enrolCheckCode.isVisible = false
binding.enrolStatus.text = getString(R.string.enrol_done, handOff.id)
binding.enrolBatteryButton.isVisible = !isIgnoringBatteryOptimisations(this)
binding.enrolDoneButton.setText(R.string.enrol_finished)
Expand All @@ -412,6 +413,13 @@ class UnlockEnrolActivity : AppCompatActivity() {
private fun shortLabel(pairing: Pairing): String =
pairing.label?.takeIf { it.isNotBlank() } ?: pairing.displayLabel().let { if (it.length > 20) it.take(12) + "…" else it }

/** The check code and its label/hint always show or hide together. */
private fun showCheckCode(visible: Boolean) {
binding.enrolCheckCodeLabel.isVisible = visible
binding.enrolCheckCode.isVisible = visible
binding.enrolCheckCodeHint.isVisible = visible
}

private fun showOnly(message: String) {
binding.enrolBody.isVisible = false
binding.enrolScanButton.isVisible = false
Expand Down
26 changes: 25 additions & 1 deletion app/src/main/res/layout/activity_unlock_enrol.xml
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,24 @@
android:textColor="@color/on_background"
tools:text="Waiting for the board…" />

<TextView
android:id="@+id/enrolCheckCodeLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"
android:fontFamily="monospace"
android:gravity="center"
android:text="@string/enrol_check_code_label"
android:textColor="@color/on_background_muted"
android:visibility="gone"
tools:visibility="visible" />

<TextView
android:id="@+id/enrolCheckCode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"
android:fontFamily="monospace"
android:letterSpacing="0.2"
android:textColor="@color/accent"
Expand All @@ -155,6 +167,18 @@
tools:text="8131"
tools:visibility="visible" />

<TextView
android:id="@+id/enrolCheckCodeHint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:fontFamily="monospace"
android:gravity="center"
android:text="@string/enrol_check_code_hint"
android:textColor="@color/on_background_muted"
android:visibility="gone"
tools:visibility="visible" />

<Button
android:id="@+id/enrolConfirmButton"
android:layout_width="wrap_content"
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@
<string name="enrol_retry">Retry</string>
<string name="enrol_waiting">Waiting for the board… If it says it added this phone but nothing arrives here, revoke the id it shows in Sapwood.</string>
<string name="enrol_received">The board added this phone. Check that the board shows the same six characters, then confirm with your phone\'s screen lock to keep the unlock key on this phone.</string>
<string name="enrol_check_code_label">Check code</string>
<string name="enrol_check_code_hint">It should match the check code on the board and in Sapwood.</string>
<string name="enrol_confirm">Confirm</string>
<string name="enrol_prompt_title">Keep the unlock key on this phone</string>
<string name="enrol_not_confirmed">Not confirmed yet. Confirm to finish; if you leave now, revoke record %1$d on the board in Sapwood.</string>
Expand Down
Loading