From f1b23ff41660ea81383545df2d8f13db2ecf5657 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 8 Nov 2024 16:37:59 +0100 Subject: [PATCH] Stop incoming ringing call when answered on another session. --- .../call/impl/utils/ActiveCallManager.kt | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt index 2b8aacc1b5..8bc77caaa3 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt @@ -109,7 +109,7 @@ class DefaultActiveCallManager @Inject constructor( // Wait for the ringing call to time out delay(ElementCallConfig.RINGING_CALL_DURATION_SECONDS.seconds) - incomingCallTimedOut() + incomingCallTimedOut(displayMissedCallNotification = true) } } @@ -117,14 +117,16 @@ class DefaultActiveCallManager @Inject constructor( * Called when the incoming call timed out. It will remove the active call and remove any associated UI, adding a 'missed call' notification. */ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - fun incomingCallTimedOut() { + fun incomingCallTimedOut(displayMissedCallNotification: Boolean) { val previousActiveCall = activeCall.value ?: return val notificationData = (previousActiveCall.callState as? CallState.Ringing)?.notificationData ?: return activeCall.value = null cancelIncomingCallNotification() - displayMissedCallNotification(notificationData) + if (displayMissedCallNotification) { + displayMissedCallNotification(notificationData) + } } override fun hungUpCall(callType: CallType) { @@ -186,28 +188,35 @@ class DefaultActiveCallManager @Inject constructor( @OptIn(ExperimentalCoroutinesApi::class) private fun observeRingingCall() { - // This will observe ringing calls and ensure they're terminated if the room call is cancelled + // This will observe ringing calls and ensure they're terminated if the room call is cancelled or if the user + // has joined the call from another session. activeCall .filterNotNull() .filter { it.callState is CallState.Ringing && it.callType is CallType.RoomCall } .flatMapLatest { activeCall -> val callType = activeCall.callType as CallType.RoomCall - // Get a flow of updated `hasRoomCall` values for the room + // Get a flow of updated `hasRoomCall` and `activeRoomCallParticipants` values for the room matrixClientProvider.getOrRestore(callType.sessionId).getOrNull() ?.getRoom(callType.roomId) ?.roomInfoFlow - ?.map { it.hasRoomCall } + ?.map { + it.hasRoomCall to (callType.sessionId in it.activeRoomCallParticipants) + } ?: flowOf() } // We only want to check if the room active call status changes .distinctUntilChanged() // Skip the first one, we're not interested in it (if the check below passes, it had to be active anyway) .drop(1) - .onEach { roomHasActiveCall -> + .onEach { (roomHasActiveCall, userIsInTheCall) -> if (!roomHasActiveCall) { // The call was cancelled timedOutCallJob?.cancel() - incomingCallTimedOut() + incomingCallTimedOut(displayMissedCallNotification = true) + } else if (userIsInTheCall) { + // The user joined the call from another session + timedOutCallJob?.cancel() + incomingCallTimedOut(displayMissedCallNotification = false) } } .launchIn(coroutineScope)