Skip to content

feat(app): copy button for the 12 recovery words on all platforms - #32

Open
Henner4746 wants to merge 1 commit into
mainfrom
feat/seed-copy-button
Open

Henner4746 wants to merge 1 commit into
mainfrom
feat/seed-copy-button

Conversation

@Henner4746

Copy link
Copy Markdown
Owner

What

A Copy button under the 12 recovery words, in every app (Android, Windows, Linux, Web):

  • on the phrase screen right after creating an identity, and
  • in Settings › Show recovery words (the dialog).

After copying, a note right under the button says: "Copied. Cleared in 60 s. Do not paste into chats or cloud notes." (German: „Kopiert. Wird in 60 s gelöscht. Nicht in Chats oder Cloud-Notizen einfügen.“). On the web the note says instead that the browser does not always let BitDM clear the clipboard.

The note sits next to the button rather than in a snack bar, because in the settings dialog a snack bar would sit behind the dialog's dim layer. The button is not highlighted (accent: false): paper is still the main way, and "I wrote them down" stays the primary button.

How it copies

New lib/core/geheime_ablage.dart talks to a native channel bitdm/ablage and falls back to Flutter's clipboard where there is none.

Platform Marking Clearing after 60 s
Android ClipDescription.EXTRA_IS_SENSITIVE: the Android 13+ preview shows dots, keyboards don't suggest it done natively in GeheimeAblage.kt. Since Android 10 only the foreground app may read the clipboard, so a Dart timer would read null while the user is in their password manager. If it can read, it clears only its own text; if it cannot (app in background), it clears without comparing.
Windows ExcludeClipboardContentFromMonitorProcessing, CanIncludeInClipboardHistory=0, CanUploadToCloudClipboard=0: kept out of Win+V history and cloud clipboard Dart compares, native leere empties the clipboard (no blank history entry)
Linux x-kde-passwordManagerHint: secret next to the text (gtk_clipboard_set_with_data): Klipper & co. skip it Dart compares, native leere releases ownership
Web none possible blind clear when the timer fires (reading would trigger a browser prompt; Firefox/Safari don't allow writing without a click). The UI says this is not reliable.

Other details:

  • The clipboard is cleared immediately when the identity is deleted (panic password, remote wipe, "delete everything"). Otherwise the words would bring back exactly what was just deleted.
  • It is not cleared on lock. The app locks when it goes to the background, which is the moment people paste into a password manager.
  • Screenshot protection is unchanged. Both views still force it while the words are visible, and copying does not touch it. On Android the sensitive flag also covers the system clipboard preview, which FLAG_SECURE does not.
  • Trust shares (Teilgeheimnisse) use the same path now, keeping their 30 s.

Tests and checks

All builds and tests ran on the Mini-PC (CT 107), none on the main PC.

  • flutter analyze --no-fatal-infos: 29 infos, all pre-existing, none in changed files.
  • New test/core/geheime_ablage_test.dart (11 cases) checks:
    • the 60 s deadline;
    • foreign clipboard content stays;
    • a second copy resets the watch;
    • raeumeJetzt clears now;
    • on Android, native code clears (no Dart timer);
    • on Windows and Linux, Dart compares and calls leere;
    • the Flutter fallback.
  • New test/oberflaeche/woerter_kopieren_test.dart (4 cases) checks:
    • the button on phrase, in both the phone and the desktop layout, passes exactly the words joined with spaces;
    • the note appears and screenshot protection stays on;
    • the button and note sit inside the settings dialog, and the dialog's Close button is still its only TextButton;
    • deleting the identity clears the clipboard.
  • Whole app suite, Linux, -j 4: 1585 passed, 5 skipped, 1 failed. The failure is geraeteabmeldung_test "4) TELEFON VERLOREN…", a warteBis timing check against a real local relay under load. It passes on its own (flutter test test/net/geraeteabmeldung_test.dart, all green) and does not touch this change.
  • New integration_test/geheime_ablage_test.dart, run against the real native code with a dummy text, never words:
    • Linux (debug build, Xvfb, xclip): TARGETS contains x-kde-passwordManagerHint, reading it gives secret, the text arrives, leere releases it, and text copied by another program stays. ✅
    • Android 15 (emulator, debug build): round trip; native clearing after the deadline; text copied in the meantime stays; raeume clears now. ✅
    • Android sensitive flag, visually: the system preview shows •••••• for the new path and the plain text for a normal copy (screenshots of a dummy text, not of the words page).
  • Windows: not built with MSVC (no Windows build machine was used).
    • geheime_ablage.cpp, flutter_window.cpp and datei_kanal.cpp compile with MinGW-w64 (-Wall -Wextra) against the Flutter client-wrapper headers.
    • The exact Win32 part of geheime_ablage.cpp ran under Wine 10: the text round-trips with umlauts, all three exclusion formats are set with the right values, leere leaves the clipboard empty, and empty text is rejected.
    • Still open: a real Windows build and a look at Win+V. flutter test integration_test/geheime_ablage_test.dart -d windows checks the formats via PowerShell.
  • flutter build web --release builds.

Not in this PR

No release, no version bump. The CHANGELOG entry sits under "2.0.2 (unveröffentlicht)".

🤖 Generated with Claude Code

Henrik wants to copy the recovery words, e.g. into a password manager,
instead of retyping or photographing them.

- A "Copy" button under the words on the 'phrase' screen after creating
  an identity and in Settings > Show recovery words (dialog). After
  copying, a note right below the button says the clipboard is cleared
  in 60 s and not to paste the words into chats or cloud notes. On the
  web the note says the browser does not always allow clearing.
- New lib/core/geheime_ablage.dart: copy through the native channel
  "bitdm/ablage" where there is one, otherwise through Flutter's
  clipboard; clear after the deadline only if the clipboard still holds
  exactly the copied text. Clears immediately when the identity is
  deleted (panic/remote wipe), not on lock (the app locks in the
  background, which is when people paste into a password manager).
- Android (GeheimeAblage.kt): ClipDescription EXTRA_IS_SENSITIVE, so the
  Android 13+ preview shows dots and keyboards do not offer it. Clearing
  runs natively because apps in the background cannot read the
  clipboard since Android 10; if it cannot read, it clears anyway.
- Windows (geheime_ablage.cpp): ExcludeClipboardContentFromMonitorProcessing,
  CanIncludeInClipboardHistory=0 and CanUploadToCloudClipboard=0, so
  neither Win+V history nor cloud clipboard keeps the words; "leere"
  empties the clipboard instead of writing an empty history entry.
- Linux (geheime_ablage.cc): offers the text plus
  x-kde-passwordManagerHint=secret via gtk_clipboard_set_with_data, so
  Klipper and similar histories skip it.
- Trust shares (Teilgeheimnisse) now copy through the same path, keeping
  their 30 s.
- Screenshot protection unchanged: both views still force it while the
  words are visible; copying does not touch it.

Tests: test/core/geheime_ablage_test.dart (deadline, foreign content
stays, Android leaves clearing to native, desktop clears via "leere"),
test/oberflaeche/woerter_kopieren_test.dart (button on 'phrase' on phone
and desktop layout, button and note inside the settings dialog, close
button unchanged, protection stays, wipe clears). Whole app suite on
Linux (CT 107): 1585 passed, 5 skipped, 1 unrelated relay timing test
under -j 4 that passes on its own. integration_test/geheime_ablage_test.dart
passed on Linux (Xvfb, xclip) and Android 15 (emulator); Windows Win32
part checked with MinGW and under Wine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Nov 9, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant