Merge pull request #3842 from element-hq/feature/bma/stopIncomingcall
Stop incoming call ringing if answered on another device.
This commit is contained in:
commit
c756ad7aee
3 changed files with 24 additions and 20 deletions
|
|
@ -8,6 +8,7 @@
|
||||||
package io.element.android.features.call.impl.utils
|
package io.element.android.features.call.impl.utils
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
import io.element.android.appconfig.ElementCallConfig
|
import io.element.android.appconfig.ElementCallConfig
|
||||||
|
|
@ -56,11 +57,6 @@ interface ActiveCallManager {
|
||||||
*/
|
*/
|
||||||
fun registerIncomingCall(notificationData: CallNotificationData)
|
fun registerIncomingCall(notificationData: CallNotificationData)
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the incoming call timed out. It will remove the active call and remove any associated UI, adding a 'missed call' notification.
|
|
||||||
*/
|
|
||||||
fun incomingCallTimedOut()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the active call has been hung up. It will remove any existing UI and the active call.
|
* Called when the active call has been hung up. It will remove any existing UI and the active call.
|
||||||
* @param callType The type of call that the user hung up, either an external url one or a room one.
|
* @param callType The type of call that the user hung up, either an external url one or a room one.
|
||||||
|
|
@ -113,18 +109,24 @@ class DefaultActiveCallManager @Inject constructor(
|
||||||
|
|
||||||
// Wait for the ringing call to time out
|
// Wait for the ringing call to time out
|
||||||
delay(ElementCallConfig.RINGING_CALL_DURATION_SECONDS.seconds)
|
delay(ElementCallConfig.RINGING_CALL_DURATION_SECONDS.seconds)
|
||||||
incomingCallTimedOut()
|
incomingCallTimedOut(displayMissedCallNotification = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun incomingCallTimedOut() {
|
/**
|
||||||
|
* 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(displayMissedCallNotification: Boolean) {
|
||||||
val previousActiveCall = activeCall.value ?: return
|
val previousActiveCall = activeCall.value ?: return
|
||||||
val notificationData = (previousActiveCall.callState as? CallState.Ringing)?.notificationData ?: return
|
val notificationData = (previousActiveCall.callState as? CallState.Ringing)?.notificationData ?: return
|
||||||
activeCall.value = null
|
activeCall.value = null
|
||||||
|
|
||||||
cancelIncomingCallNotification()
|
cancelIncomingCallNotification()
|
||||||
|
|
||||||
displayMissedCallNotification(notificationData)
|
if (displayMissedCallNotification) {
|
||||||
|
displayMissedCallNotification(notificationData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hungUpCall(callType: CallType) {
|
override fun hungUpCall(callType: CallType) {
|
||||||
|
|
@ -186,28 +188,35 @@ class DefaultActiveCallManager @Inject constructor(
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
private fun observeRingingCall() {
|
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
|
activeCall
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.filter { it.callState is CallState.Ringing && it.callType is CallType.RoomCall }
|
.filter { it.callState is CallState.Ringing && it.callType is CallType.RoomCall }
|
||||||
.flatMapLatest { activeCall ->
|
.flatMapLatest { activeCall ->
|
||||||
val callType = activeCall.callType as CallType.RoomCall
|
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()
|
matrixClientProvider.getOrRestore(callType.sessionId).getOrNull()
|
||||||
?.getRoom(callType.roomId)
|
?.getRoom(callType.roomId)
|
||||||
?.roomInfoFlow
|
?.roomInfoFlow
|
||||||
?.map { it.hasRoomCall }
|
?.map {
|
||||||
|
it.hasRoomCall to (callType.sessionId in it.activeRoomCallParticipants)
|
||||||
|
}
|
||||||
?: flowOf()
|
?: flowOf()
|
||||||
}
|
}
|
||||||
// We only want to check if the room active call status changes
|
// We only want to check if the room active call status changes
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
// Skip the first one, we're not interested in it (if the check below passes, it had to be active anyway)
|
// Skip the first one, we're not interested in it (if the check below passes, it had to be active anyway)
|
||||||
.drop(1)
|
.drop(1)
|
||||||
.onEach { roomHasActiveCall ->
|
.onEach { (roomHasActiveCall, userIsInTheCall) ->
|
||||||
if (!roomHasActiveCall) {
|
if (!roomHasActiveCall) {
|
||||||
// The call was cancelled
|
// The call was cancelled
|
||||||
timedOutCallJob?.cancel()
|
timedOutCallJob?.cancel()
|
||||||
incomingCallTimedOut()
|
incomingCallTimedOut(displayMissedCallNotification = true)
|
||||||
|
} else if (userIsInTheCall) {
|
||||||
|
// The user joined the call from another session
|
||||||
|
timedOutCallJob?.cancel()
|
||||||
|
incomingCallTimedOut(displayMissedCallNotification = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.launchIn(coroutineScope)
|
.launchIn(coroutineScope)
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ class DefaultActiveCallManagerTest {
|
||||||
onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(addMissedCallNotificationLambda = addMissedCallNotificationLambda)
|
onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(addMissedCallNotificationLambda = addMissedCallNotificationLambda)
|
||||||
)
|
)
|
||||||
|
|
||||||
manager.incomingCallTimedOut()
|
manager.incomingCallTimedOut(displayMissedCallNotification = true)
|
||||||
|
|
||||||
addMissedCallNotificationLambda.assertions().isNeverCalled()
|
addMissedCallNotificationLambda.assertions().isNeverCalled()
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +139,7 @@ class DefaultActiveCallManagerTest {
|
||||||
manager.registerIncomingCall(aCallNotificationData())
|
manager.registerIncomingCall(aCallNotificationData())
|
||||||
assertThat(manager.activeCall.value).isNotNull()
|
assertThat(manager.activeCall.value).isNotNull()
|
||||||
|
|
||||||
manager.incomingCallTimedOut()
|
manager.incomingCallTimedOut(displayMissedCallNotification = true)
|
||||||
advanceTimeBy(1)
|
advanceTimeBy(1)
|
||||||
|
|
||||||
assertThat(manager.activeCall.value).isNull()
|
assertThat(manager.activeCall.value).isNull()
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
||||||
class FakeActiveCallManager(
|
class FakeActiveCallManager(
|
||||||
var registerIncomingCallResult: (CallNotificationData) -> Unit = {},
|
var registerIncomingCallResult: (CallNotificationData) -> Unit = {},
|
||||||
var incomingCallTimedOutResult: () -> Unit = {},
|
|
||||||
var hungUpCallResult: (CallType) -> Unit = {},
|
var hungUpCallResult: (CallType) -> Unit = {},
|
||||||
var joinedCallResult: (CallType) -> Unit = {},
|
var joinedCallResult: (CallType) -> Unit = {},
|
||||||
) : ActiveCallManager {
|
) : ActiveCallManager {
|
||||||
|
|
@ -25,10 +24,6 @@ class FakeActiveCallManager(
|
||||||
registerIncomingCallResult(notificationData)
|
registerIncomingCallResult(notificationData)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun incomingCallTimedOut() {
|
|
||||||
incomingCallTimedOutResult()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hungUpCall(callType: CallType) {
|
override fun hungUpCall(callType: CallType) {
|
||||||
hungUpCallResult(callType)
|
hungUpCallResult(callType)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue