fix: WebRTC call bugs and add Call Settings screen

Bug fixes:
- Fix invitePeer() bypassing CallManager state tracking, causing
  invited peers to not appear in pendingPeerPubKeys
- Remove 10-minute proximity wake lock timeout so it lasts the
  full call duration (released on cleanup)
- Send hangup to peers on caller timeout so callees stop ringing
  immediately instead of waiting for their own 60s timeout
- Remove duplicate cleanup() call on Ended→Idle transition

New feature:
- Add Call Settings screen (TURN servers + video quality)
- Users can configure custom TURN servers for restrictive networks
- Default STUN/TURN servers are always active and displayed
- Video resolution options: 480p, 720p (default), 1080p
- Configurable max video bitrate: 750kbps, 1.5Mbps, 3Mbps
- Settings wired into IceServerConfig and CallMediaManager

https://claude.ai/code/session_01F5RF2yzngiMr1v2gr7f1GP
This commit is contained in:
Claude
2026-04-10 17:18:25 +00:00
parent 8813907bd3
commit cd573a583c
12 changed files with 521 additions and 24 deletions
@@ -765,22 +765,32 @@ class CallManager(
timeoutJob =
scope.launch {
delay(CALL_TIMEOUT_MS)
val current = _state.value
val currentCallId =
when (current) {
is CallState.Offering -> current.callId
is CallState.IncomingCall -> current.callId
else -> null
}
if (currentCallId == callId) {
val peerPubKeys =
val peerPubKeys: Set<HexKey>
val wasOffering: Boolean
stateMutex.withLock {
val current = _state.value
val currentCallId =
when (current) {
is CallState.Offering -> current.callId
is CallState.IncomingCall -> current.callId
else -> null
}
if (currentCallId != callId) return@launch
peerPubKeys =
when (current) {
is CallState.Offering -> current.peerPubKeys
is CallState.IncomingCall -> current.peerPubKeys()
else -> return@launch
}
wasOffering = current is CallState.Offering
transitionToEnded(callId, peerPubKeys, EndReason.TIMEOUT)
}
// Notify peers so their phones stop ringing immediately
// instead of waiting for their own 60-second timeout.
if (wasOffering && peerPubKeys.isNotEmpty()) {
val result = factory.createGroupHangup(peerPubKeys, callId, signer = signer)
result.wraps.forEach { publishEvent(it) }
}
}
}