Skip to content

feat: home screen hardware wallet ui#999

Draft
ovitrif wants to merge 37 commits into
masterfrom
feat/home-hw-wallet-ui
Draft

feat: home screen hardware wallet ui#999
ovitrif wants to merge 37 commits into
masterfrom
feat/home-hw-wallet-ui

Conversation

@ovitrif

@ovitrif ovitrif commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Part of #998

This PR brings the first slice of the Trezor hardware wallet epic to the wallet home screen. A paired Trezor now shows up as a watch-only balance: a device row under the Savings/Spending tiles, its sats folded into the headline total, and its on-chain transactions merged into the recent activity list. A new Hardware suggestion card prompts users with no paired device to start connecting.

Description

  • Adds a full-width hardware-wallet row beneath the Savings/Spending tiles showing the device name, a real-time Bluetooth/USB connection indicator (green when linked, grey otherwise), a blue bitcoin icon, and the watch-only balance. The hardware balance is included in the headline total.
  • Merges the device's on-chain activity into the home recent-activity list and the All Activity screen, with blue hardware-wallet icon variants distinguishing those rows from regular on-chain activity. The All Activity filters apply to hardware items too: Sent/Received tabs and date range match them, while tag filters and the Other tab exclude them (no tags, no transfers yet).
  • Adds the Hardware / Connect device suggestion card, shown only while no device is paired. Tapping it opens the connect-flow sheet with the final intro screen per Figma (Trezor + Ledger visuals, localized copy, Cancel) — Continue stays disabled until the connect flow lands in its dedicated subtask. Tapping the hardware row shows a not-yet-implemented notice for the hardware overview screen (also a later subtask).
  • Opens the standard activity detail for hardware-wallet items tapped on the home list, resolving them from the watch-only data when they are not in the activity database (blue icon included).
  • Introduces a watch-only data layer: balances and activity stream from the on-chain xpub watcher from feat: add trezor onchain event watcher #976, with one watcher per monitored address type per device (honouring Settings ▸ Advanced ▸ Address Type, same as the on-chain wallet), aggregated per device. Account xpubs for all address types are captured on connect and persisted, so balances show while the device is disconnected and toggling a type on later starts watching it without reconnecting.
  • Fixes stale Trezor connection status by detecting the phone's Bluetooth being switched off and USB unplug, so the indicator reflects reality in real time. Disconnect events are now observed for the whole app lifetime — previously they were only collected while the dev Trezor screen was open.
  • Auto-reconnects to a known device when its transport comes back: Bluetooth re-enabled (adapter broadcast) or the Trezor plugged back in (USB attach is an activity intent delivered via the OS app picker, handled in the main activity — tapping "Always" makes future replugs fully automatic). Stored pairing credentials make the connect prompt-free, so the indicator recovers to green on its own.
  • Fixes the Trezor restarting PIN/pairing entry mid-typing: a transport event (USB attach, Bluetooth on) could trigger auto-reconnect during a live handshake, whose half-open session looks identical to a stale one, so the reset dropped the session being authenticated. Auto-reconnect now backs off while any connect is in progress or the device awaits PIN/pairing input — including connects started from the dev Trezor screen. Reconnect triggers are also serialized into a single in-flight retry loop: a Trezor re-enumerates USB during its unlock flow, so one replug delivers several attach intents, and uncoordinated loops kept restarting the device's PIN entry for the first seconds after plugging in. Reconnects also prefer the transport that came back, so a USB replug does not reconnect over BLE when the same device is paired on both.
  • Fixes a native crash (SIGSEGV in libusbhost) when a USB-connected device responded after a read timeout: timed-out async UsbRequests were closed while their URB was still queued in the kernel, which then wrote into freed memory once data arrived. USB reads/writes now use synchronous bulkTransfer, eliminating the request-lifetime race.
  • Adds a Pair Device sheet shown app-wide whenever the device requests its one-time security code mid-connect (pairing credentials are per transport, so e.g. the first USB connect after BLE pairing asks again): instruction text, visible 6-digit code entry in fixed-width cells (digits replace placeholder dots without layout shifts) on the app numpad — hardware keyboards work too — auto-submit when complete, swipe-down cancels the request. The dev Trezor screen now uses this sheet too — its old pairing dialog is removed.
  • Shows the received sheet when a watcher picks up a new inbound transaction to a hardware-wallet address. The initial history sync after a watcher starts is treated as a baseline, so only transactions arriving while watching trigger the sheet.
  • Syncs suggestion cards with the v61 design: Invite now uses the green tint instead of blue, Profile uses the brand tint, and Hardware sits before Shop in the default set.
  • Shows the same physical device paired over both bluetooth and usb as one wallet, identified by its xpubs, so its balance and activity are counted once. The default tile name is the vendor-prefixed model (e.g. "Trezor Safe 7") unless a custom on-device label is set.
  • General UI: sheets now reserve the full home toolbar block plus clearance instead of overlapping its bottom edge, so the toolbar stays visible above large sheets app-wide (Receive, hardware connect, etc.).

Preview

BLE USB
trezorBle2x.mp4
trezorUsb2x.mp4
Receive Suggestions
receive2x.mp4

Edge Cases & State Variants

Same Trezor 2x BLE+USB Activity, Icons, Sheet
trezorViaBle.Usb2x.mp4
trezorActivity2x.mp4

QA Notes

Manual Tests

  • 1. No paired device → Home: no hardware row, Hardware device suggestion card shown.
  • 2. Tap the Hardware suggestion card: connect intro sheet opens (device visuals, Cancel works, Continue disabled).
  • 3. Connect a Trezor device (dev Trezor screen or Bridge emulator) → Home: hardware row appears with device name, blue BTC icon and balance; headline total includes the hardware balance; recent device transactions appear in the activity list with blue icons.
  • 4a. BLE-connected device → disable phone Bluetooth: indicator turns grey, balance stays visible.
    • 4b. re-enable Bluetooth: indicator turns green again on its own (auto-reconnect, no pairing prompt).
  • 5a. USB-connected device → unplug it: indicator turns grey, balance stays visible.
    • 5b. plug it back in: indicator turns green again on its own (auto-reconnect, no pairing prompt).
    • 5c. if the device asks for its one-time security code: Pair Device sheet opens with the app numpad; entering the code on-screen completes the connect; swipe-down dismiss cancels it.
    • 5d. same device also paired over BLE → replug USB: home tile shows the usb indicator green, not bluetooth.
  • 6. Tap the hardware row: not-yet-implemented toast shows.
  • 7. Tap a hardware activity item in the home list: Activity Detail opens with a blue icon.
  • 8. Watcher running → send sats to a trezor address from an external wallet: received sheet pops up once picked up; details button opens the activity.
  • 9. Pair the same Trezor over BLE and USB: home shows one tile named "Trezor ", balance and activity counted once; indicator reflects the connected transport (USB or BLE).
  • 10. Show All → All Activity: hardware activities listed with blue icons; Sent/Received tabs filter them; tag filter hides them.
  • 11. regression: open Receive and other large sheets: sheet top sits below the home toolbar, toolbar fully visible.

Automated Checks

  • Unit tests added: cover watch-only balance/activity aggregation per device, HistoryTransaction to Activity mapping, address-type monitor filtering, and the Env network pin in HwWalletRepoTest.kt.
  • Unit tests added: cover the per-device hardware balances and total getters in BalanceStateTest.kt.
  • Unit tests updated: wire the new HwWalletRepo dependency into WalletRepoTest.kt and DeriveBalanceStateUseCaseTest.kt.
  • Unit tests added: cover the hardware-wallet fallback and not-found path of activity detail loading in ActivityDetailViewModelTest.kt.
  • Unit tests added: cover received-tx detection (baseline sync, new inbound, outbound ignored, no re-emission) in HwWalletRepoTest.kt.
  • Unit tests added: cover cross-transport dedup (single wallet, single watcher, connected entry wins) and vendor-prefixed default names in HwWalletRepoTest.kt.
  • Unit tests added: cover external-disconnect clearing the connected device without any screen observing in TrezorRepoTest.kt.
  • Unit tests added: cover transport-restored auto-reconnect and its already-connected guard in TrezorRepoTest.kt. Also cover scan retries until the device advertises again and stale-session reset before scanning. The usb-attach intent chain is covered across TrezorRepoTest.kt, HwWalletRepoTest.kt, and AppViewModelSendFlowTest.kt.
  • Unit tests added: cover the auto-reconnect back-off during pending PIN/pairing entry, the single-loop serialization of repeated reconnect triggers, and the restored-transport preference in TrezorRepoTest.kt.
  • Unit tests added: cover the Pair Device sheet trigger (show/hide, high-priority sheet guard) and the pairing-code passthroughs in AppViewModelSendFlowTest.kt and HwWalletRepoTest.kt.
  • Unit tests added: cover the All Activity merge (newest-first ordering, tab filtering, tag-filter exclusion, hardware id exposure) in ActivityListViewModelTest.kt.
  • Ran locally: just compile, just test (full suite), just lint.

@ovitrif ovitrif added this to the 2.4.0 milestone Jun 9, 2026
@ovitrif ovitrif self-assigned this Jun 9, 2026
@ovitrif ovitrif force-pushed the feat/home-hw-wallet-ui branch from 0a23adb to b56896b Compare June 9, 2026 13:38
@ovitrif ovitrif force-pushed the feat/home-hw-wallet-ui branch from 490121b to a4dbd43 Compare June 9, 2026 15:18
@ovitrif

ovitrif commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator Author

@greptile review

@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces the first slice of the Trezor hardware wallet epic: a watch-only balance/activity layer backed by per-(device, address-type) xpub watchers, a new hardware-wallet row beneath the Savings/Spending tiles, headline-total inclusion, merged recent-activity, and a stub connect sheet opened from a new HARDWARE suggestion card.

  • HwWalletRepo (new): aggregates watcher events per device into hardwareWallets, totalHardwareSats, and hardwareActivities flows; wired into WalletRepo, HomeViewModel, and ActivityListViewModel.
  • TrezorTransport (updated): registers a BroadcastReceiver for ACTION_STATE_CHANGED (BT off) and ACTION_USB_DEVICE_DETACHED to surface link-loss events the per-connection GATT callback misses, keeping the UI indicator accurate in real time.
  • TrezorRepo (updated): captures account-level xpubs for all address types on connect via fetchAccountXpubs and persists them in KnownDevice, enabling balance tracking without the device present.

Confidence Score: 3/5

The data layer and UI wiring are solid, but the stub connect sheet ships production-visible hardcoded English strings (including a 'not yet implemented' message) to users who tap the Hardware suggestion card, and a watcher-stop failure path in HwWalletRepo can cause stale balance data to persistently re-appear.

The core watch-only balance and activity plumbing is well-structured and backed by unit tests. The main concern before shipping is HardwareWalletConnectSheet.kt: three user-facing strings are hardcoded in English and bypass localisation, and the copy tells users the feature is 'not yet implemented' — which is confusing in a production release. Additionally, HwWalletRepo.syncWatchers silently discards the stopWatcher result, so a failed stop leaves the watcher active; observeWatcherEvents will continue writing its balance updates back into _watcherData, making the entry impossible to evict until the next settings change triggers a new sync cycle.

HardwareWalletConnectSheet.kt needs production-ready copy and string resources before shipping; HwWalletRepo.kt syncWatchers needs the stopWatcher result checked; HomeViewModel.kt emptyWalletSuggestions is missing the HARDWARE card.

Important Files Changed

Filename Overview
app/src/main/java/to/bitkit/repositories/HwWalletRepo.kt New singleton that aggregates watch-only balances and activity per device; core logic is sound but stopWatcher result is silently discarded, leaving a window where stale watchers can re-populate _watcherData.
app/src/main/java/to/bitkit/ui/sheets/HardwareWalletConnectSheet.kt Stub connect sheet with three hardcoded English user-visible strings that should be in strings.xml per AGENTS.md; one string explicitly says 'not yet implemented' and will be shown to production users.
app/src/main/java/to/bitkit/ui/screens/wallets/HomeViewModel.kt Wires hardware wallets into the suggestion flow correctly for spending/savings-only states, but emptyWalletSuggestions is not updated and never includes the HARDWARE card.
app/src/main/java/to/bitkit/repositories/TrezorRepo.kt Adds fetchAccountXpubs on connect and persists xpubs in KnownDevice; failures are swallowed per-type with a warning, fallback to previous xpubs is handled correctly.
app/src/main/java/to/bitkit/services/TrezorTransport.kt Adds a BroadcastReceiver for BT state-change and USB detach events to surface link loss that GATT misses; registration uses RECEIVER_NOT_EXPORTED correctly.
app/src/main/java/to/bitkit/repositories/WalletRepo.kt Wires hwWalletRepo.totalHardwareSats into _balanceState and preserves live hardware total on syncBalances; the syncBalances path correctly uses .value to avoid dropping updates.
app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt Adds hardware wallet grid rows beneath Savings/Spending tiles and wires totalWithHardwareSats to the headline total; UI refactoring is clean and the grid layout handles 1, 2, and 3+ devices.
app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt Merges hardware wallet activity into the home activity list, sorted by timestamp and capped at the same limit; the null-initial-state handling is correct.
app/src/main/java/to/bitkit/models/BalanceState.kt Adds totalHardwareSats field and totalWithHardwareSats computed property using safe() arithmetic to prevent overflow; covered by new unit tests.
app/src/test/java/to/bitkit/repositories/HwWalletRepoTest.kt Good coverage of the aggregation logic (zero-balance init, multi-watcher sum, address-type filtering, and activity mapping); uses test dispatcher correctly.

Sequence Diagram

sequenceDiagram
    participant TT as TrezorTransport
    participant TR as TrezorRepo
    participant HW as HwWalletRepo
    participant WR as WalletRepo
    participant HVM as HomeViewModel
    participant ALV as ActivityListViewModel

    TT->>TR: externalDisconnect (BT off / USB unplug)
    TR-->>HW: state (connectedDeviceId cleared)
    HW-->>HVM: "hardwareWallets (isConnected=false)"

    TR->>TR: addOrUpdateKnownDevice (on connect)
    TR->>TR: fetchAccountXpubs()
    TR-->>HW: trezorStore.data (KnownDevice with xpubs)
    HW->>TR: startWatcher(watcherId, xpub, accountType)
    TR-->>HW: watcherEvents (TransactionsChanged)
    HW->>HW: _watcherData.update (balance + activities)

    HW-->>WR: totalHardwareSats
    WR->>WR: _balanceState.update(copy(totalHardwareSats))

    HW-->>HVM: hardwareWallets
    HVM->>HVM: _uiState.update(copy(hardwareWallets))

    HW-->>ALV: hardwareActivities
    ALV->>ALV: combine(localActivities, hardwareActivities) → latestActivities
Loading

Comments Outside Diff (1)

  1. app/src/main/java/to/bitkit/ui/screens/wallets/HomeViewModel.kt, line 372-386 (link)

    P2 HARDWARE suggestion absent for empty-wallet users

    emptyWalletSuggestions is the only suggestion builder that does not receive hasHardwareWallet and does not include Suggestion.HARDWARE.takeIf { !hasHardwareWallet }. A user who has no savings and no spending balance — the "empty wallet" state — will therefore never see the Hardware / Connect device card, even though they could still own a Trezor they want to track. The two other builders (spendingSuggestions, savingsOnlySuggestions) both include the card, making the omission asymmetric and likely unintentional.

Reviews (1): Last reviewed commit: "docs: reference test files by name in pr..." | Re-trigger Greptile

Comment thread app/src/main/java/to/bitkit/ui/sheets/HardwareSheet.kt Outdated
Comment thread app/src/main/java/to/bitkit/repositories/HwWalletRepo.kt Outdated
@ovitrif ovitrif mentioned this pull request Jun 9, 2026
28 tasks
@ovitrif ovitrif force-pushed the feat/home-hw-wallet-ui branch from 1d82489 to b301a77 Compare June 10, 2026 08:26
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