feat(shared): Android experimental saved-devices KMP UI

Gate pairing and targeted transfers behind Settings → Experimental, with
CoreGateway wake-ups, in-flow prompts, and one Android dogfood round-trip.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-11 18:09:37 +02:00
parent edf7426672
commit eabc2754a7
43 changed files with 2993 additions and 36 deletions

View File

@@ -0,0 +1,44 @@
# Saved devices UI contract (KMP + Apple)
Experimental product UI for saved devices and targeted transfers. Rust UniFFI is the source of behaviour; this document keeps Apple and KMP interaction semantics aligned.
Status: experimental (default off in KMP Settings).
## Vocabulary
Use **saved device**, **device relationship**, **targeted transfer**, **invitation transfer**. Do not say contact, person, or account in UI copy.
## Experimental gate
- KMP Android: Settings → Experimental → Saved devices, preference default **off**, persisted.
- KMP desktop: experimental UI **hidden**.
- Apple: gate shape is platform-owned; semantics below still apply when the feature is enabled.
## Events are wake-ups
`pairing` and `targeted_transfer` core events (and KMP `CoreSignal.PairingChanged` / `TargetedTransferChanged`) mean: refresh durable state via list/get APIs. Do not treat event payloads as authoritative storage. Deduplicate by event id/revision when needed.
Relevant kinds:
- **pairing:** `eligibility-available`, `eligibility-removed`, `relationship-changed`, `relationship-grant-rotated`, `saved-device-forgotten`, `device-blocked`
- **targeted_transfer:** `offer-received`, `offer-accepted`, `offer-declined`
## Pairing
After a completed invitation transfer, eligibility may exist. The user may accept or decline. Mutual consent yields a saved device. Pending eligibility/relationship state must remain reachable if an in-flow prompt is dismissed.
## Targeted approve / pull
1. List pending targeted offers (or react to `offer-received`).
2. `respond_to_targeted_offer(transfer_id, accepted)` → typed outcome only:
- `Approved { transfer_id }`
- `Declined`
- `AlreadySettled { transfer_id }`
3. Never accept or display authorization/grant strings across the public binding.
4. Pull / resume with **transfer id + destination** (path or output sink). Android KMP reuses the invitation MediaStore Downloads sink for the experimental MVP receive.
## Out of this contracts MVP chrome
Resume / cancel / delete / grant-rotate UI, background notifications, and mid-transfer progress polish may follow without changing the approve/pull rules above.
Block is available from the Saved devices area. Unblock is not required for the KMP MVP chrome when the product surface does not already expose blocked-device management.