Transfer Screen improvements:
- Add QR code scanning for recipient address (using zxing library)
- Support EIP-681 URI format (ethereum:0x...) and plain address
- Remove password requirement - TSS wallets don't need passwords
- Remove unused onScanQrCode callback parameter
WalletsScreen changes:
- Simplify onTransfer callback to only pass shareId
- Remove TransferDialog - now navigates directly to TransferScreen
- Remove unused state variables (showTransferDialog, transferWallet)
Bug fix:
- Remove 0x prefix from message_hash before sending to API
- Backend expects pure hex, not 0x-prefixed hex string
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Participant class is already imported via domain.model.* wildcard import,
no need for separate import from data.repository (where it doesn't exist).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- TssRepository: Use address-based wallet name since ShareRecordEntity
doesn't have wallet_name field (unlike Electron's ShareRecord)
- MainViewModel: Add missing Participant import and simplify type reference
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changes:
- Fix Android state not resetting after successful keygen/join
- Add resetSessionStatus() method in TssRepository
- Call reset on success navigation in MainActivity
- Make Android co-sign flow 100% consistent with Electron:
- Get keygen session status for participants list
- Filter out co-managed-party-* (server backup parties)
- Auto-join via gRPC after creating sign session
- Start message routing BEFORE signing (prepareForSign)
- Use gRPC response partyIndex instead of local share
- Use original keygen thresholdN instead of signingParties.size
- Pass parties list in join sign flow
- Update SignSessionInfoResponse to include parties array
- Update validateSignInviteCode to parse parties from API response
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add AppSettingEntity and AppSettingDao to Database.kt for key-value storage
- Add database migration (version 1 → 2) to create app_settings table
- Modify TssRepository.registerParty() to load/create partyId from database
- PartyId is now persisted across app restarts, matching Electron's getOrCreatePartyId()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add warning log when parties miss session event broadcast (message-router)
- Add logging for subscribeSessionEvents to detect null asyncStub
- Add sessionStatusPollingJob field for future fallback polling mechanism
This helps diagnose why Android parties are not receiving session_started events.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Problem:
- Android initiator/joiner could miss session_started events due to race condition
- Events arriving between joinSession() and _currentSession.value assignment were ignored
- This caused keygen timeout because parties never started the TSS protocol
Solution:
- Add pendingSessionId field set BEFORE joinSession() call
- Modify startSessionEventSubscription() to match events against both activeSession and pendingSessionId
- Clear pendingSessionId on session completion, failure, or cancellation
This ensures session_started events are correctly processed even if they arrive
before _currentSession is fully initialized.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add getPartyId() method to TssRepository
- Call getSessionStatus after createKeygenSession to fetch all participants
including server-party-co-managed that have already auto-joined
- This matches Electron's behavior of calling getSessionStatus on session page
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Backend changes (session-coordinator):
- Add PublishParticipantJoined method to JoinSessionMessageRouterClient interface
- Implement PublishParticipantJoined in MessageRouterClient to broadcast events
- Call PublishParticipantJoined in join_session.go after participant joins
- Add detailed logging for debugging event broadcast
Android changes (service-party-android):
- Add detailed logging in TssRepository for session event handling
- Add detailed logging in MainViewModel for participant_joined processing
- Log activeSession state, event matching, and participant updates
This enables the initiator's waiting screen to receive real-time updates
when participants join the session, matching the expected behavior.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Created PortraitCaptureActivity that extends CaptureActivity
- Registered it in AndroidManifest.xml with screenOrientation="portrait"
- Updated JoinKeygenScreen and CoSignJoinScreen to use the portrait activity
- Also simplified keygen join logic to match Electron exactly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Removed polling fallback and simplified to match Electron's design:
- If joinSession returns sessionStatus="in_progress", trigger keygen immediately
- Otherwise wait for session_started gRPC event
Added debug log to show sessionStatus value for troubleshooting.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changed from single 2-second delay to 5 attempts at 500ms intervals.
This provides faster detection while covering a longer window (2.5 seconds total).
The polling loop:
- Checks every 500ms for up to 5 times
- Stops immediately if keygen is already triggered
- Stops if session context changes (user cancelled/navigated away)
This handles the case where the last joiner triggers session_started
but cannot receive the event themselves.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When multiple Android devices join a keygen session nearly simultaneously,
the last joiner may miss the session_started gRPC event because it's sent
before the device has fully set up its event subscription.
This fix adds a 2-second delayed polling check after join to detect if
the session has already started. If the session is in_progress and we
haven't started keygen yet, trigger it via polling instead of relying
solely on the session_started event.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changes:
- Change address derivation from deriveKavaAddress to deriveEvmAddress
in TssRepository.kt (3 locations)
- Add AddressUtils.isEvmAddress() and getEvmAddress() helper methods
to handle both old Cosmos and new EVM address formats
- Fix balance query for old wallets by deriving EVM address from
public key when needed (MainViewModel.fetchBalanceForShare)
- Add retry logic for optimistic lock conflicts in join_session.go
to prevent party_index collision during concurrent joins
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Major improvements to Android gRPC client:
- Add automatic reconnection with exponential backoff (1s to 30s)
- Add heartbeat mechanism with failure detection (30s interval, 3 failures trigger reconnect)
- Add stream version tracking to filter stale callbacks
- Add channel state monitoring (every 5s)
- Add per-call deadline instead of one-time deadline for stubs
- Add SharedFlow for connection events (Connected, Disconnected, Reconnecting, Reconnected, PendingMessages)
- Add callback exception handling for robustness
- Add stream recovery after reconnection via callback mechanism
TssRepository changes:
- Save message routing params for recovery after reconnect
- Expose grpcConnectionEvents SharedFlow for UI notifications
- Auto-restore event subscriptions after reconnection
Other changes:
- Add QR code to Electron Create page for mobile scanning
- Auto version increment from version.properties
- SettingsScreen shows BuildConfig version info
- CreateWalletScreen tracks hasEnteredSession state
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The server validates device_type and only accepts specific values.
Electron doesn't send device_info at all, which passes validation.
Match that behavior for consistency.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The app was crashing with FRAME_SIZE_ERROR because the gRPC client
was using plaintext mode when connecting to port 443 (TLS endpoint).
This caused the client to receive encrypted data that it couldn't parse.
Fix: Use useTransportSecurity() for port 443, usePlaintext() for other ports.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Theme changes:
- Replace green theme with dark gray & gold color scheme
- Primary color: Gold (#D4AF37)
- Background: Dark gray (#1A1A1A)
- Surface: Medium gray (#2D2D2D)
- Disable dynamic colors to enforce custom theme
- Default to dark theme for best visual impact
- Update success indicators from green to gold across screens
JoinKeygen flow fixes (100% Electron compatible):
- Add onResetState callback for proper state reset
- Cancel in confirm/joining/progress resets to input state (stays on page)
- Two-step flow: joinKeygenSessionViaGrpc + executeKeygenAsJoiner
- Wait for session_started event before executing keygen
CoSign flow fixes (100% Electron compatible):
- Add onResetState callback and QR scanner support
- Add three-button layout (Cancel, Back, Join) in select_share step
- Two-step flow: joinSignSessionViaGrpc + executeSignAsJoiner
- If session already in_progress, trigger sign immediately (Solution B)
- Wait for session_started event otherwise
Repository changes:
- Add joinKeygenSessionViaGrpc and executeKeygenAsJoiner methods
- Add joinSignSessionViaGrpc and executeSignAsJoiner methods
- Add JoinKeygenViaGrpcResult and JoinSignViaGrpcResult data classes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Matching Electron app functionality:
1. Copy address button:
- Shows "✓ 已复制" feedback after copying
- Auto-resets after 2 seconds
2. Explorer link button (new):
- Opens address in Kava block explorer
- Uses correct URL based on network type:
- Mainnet: kavascan.com
- Testnet: testnet.kavascan.com
Changes:
- WalletsScreen: Added networkType parameter
- WalletDetailDialog: Added copy feedback state and explorer button
- MainActivity: Pass networkType to WalletsScreen
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Both Electron and Android apps now calculate the maximum transferable
amount by subtracting estimated gas fees from the balance:
Electron (Home.tsx):
- Added calculateMaxAmount() async function that fetches gas price
- Uses 21000 gas limit for simple transfers
- Shows loading state while calculating
Android (TransferScreen.kt):
- Added calculateMaxTransferAmount() in TransactionUtils
- Uses coroutine to fetch gas price asynchronously
- Shows "..." while calculating, falls back to balance on error
Both implementations:
- Add 10% buffer to gas price for safety
- Round down to 6 decimal places
- Show error if balance insufficient for gas
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Major changes:
- Add complete Android app (service-party-android) with Jetpack Compose UI
- Implement real account-service API calls for keygen and sign sessions:
- POST /api/v1/co-managed/sessions (create keygen session)
- GET /api/v1/co-managed/sessions/by-invite-code/{code} (validate invite)
- POST /api/v1/co-managed/sessions/{id}/join (join keygen session)
- POST /api/v1/co-managed/sign (create sign session)
- GET /api/v1/co-managed/sign/by-invite-code/{code} (validate sign invite)
- POST /api/v1/co-managed/sign/{id}/join (join sign session)
- Add QR code generation and scanning for session invites
- Remove password requirement (use empty string)
- Add floating action button for wallet creation
- Add network type aware explorer links (mainnet/testnet)
Network configuration:
- Change default network to Kava mainnet for both Electron and Android apps
- Electron: main.ts, transaction.ts, Settings.tsx, Layout.tsx
- Android: Models.kt (NetworkType.MAINNET default)
Features:
- Full TSS keygen and sign protocol via gomobile bindings
- gRPC message routing for multi-party communication
- Cross-platform compatibility with service-party-app (Electron)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>