diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/call/CallManager.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/call/CallManager.kt index 581969506..c77ba16b9 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/call/CallManager.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/call/CallManager.kt @@ -108,6 +108,12 @@ class CallManager( _state.value = CallState.Connecting(current.callId, current.callerPubKey, current.callType) cancelTimeout() publishEvent(result.wrap) + + // Notify other devices of this user that the call was answered here. + // This gift-wraps an answer event to our own pubkey so other logged-in + // devices see it and stop ringing. + val selfNotify = factory.createCallAnswer(sdpAnswer, signer.pubKey, current.callId, signer) + publishEvent(selfNotify.wrap) } suspend fun rejectCall() { @@ -117,6 +123,10 @@ class CallManager( val result = factory.createReject(current.callerPubKey, current.callId, signer = signer) transitionToEnded(current.callId, current.callerPubKey, EndReason.REJECTED) publishEvent(result.wrap) + + // Notify other devices of this user that the call was rejected here. + val selfNotify = factory.createReject(signer.pubKey, current.callId, signer = signer) + publishEvent(selfNotify.wrap) } fun onCallAnswered(event: CallAnswerEvent) { @@ -131,6 +141,12 @@ class CallManager( onAnswerReceived?.invoke(event) } + is CallState.IncomingCall -> { + // Another device of this user answered the call — stop ringing. + if (callId != current.callId) return + transitionToEnded(current.callId, current.callerPubKey, EndReason.ANSWERED_ELSEWHERE) + } + is CallState.Connected -> { // Renegotiation answer (e.g., peer accepted our video upgrade offer) if (callId != current.callId) return @@ -145,10 +161,24 @@ class CallManager( fun onCallRejected(event: CallRejectEvent) { val current = _state.value - if (current !is CallState.Offering) return - if (event.callId() != current.callId) return + val callId = event.callId() - transitionToEnded(current.callId, current.peerPubKey, EndReason.PEER_REJECTED) + when (current) { + is CallState.Offering -> { + if (callId != current.callId) return + transitionToEnded(current.callId, current.peerPubKey, EndReason.PEER_REJECTED) + } + + is CallState.IncomingCall -> { + // Another device of this user rejected the call — stop ringing. + if (callId != current.callId) return + transitionToEnded(current.callId, current.callerPubKey, EndReason.REJECTED) + } + + else -> { + return + } + } } fun onIceCandidate(event: CallIceCandidateEvent) { diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/call/CallState.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/call/CallState.kt index 79334a527..65169aa4c 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/call/CallState.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/call/CallState.kt @@ -68,4 +68,5 @@ enum class EndReason { ERROR, PEER_HANGUP, PEER_REJECTED, + ANSWERED_ELSEWHERE, }