Merge pull request #4029 from vector-im/feature/fga/fix_voip_issues
Feature/fga/fix voip issues
This commit is contained in:
commit
863ba609df
1
changelog.d/4019.bugfix
Normal file
1
changelog.d/4019.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Fix sticky end call notification
|
1
changelog.d/4026.bugfix
Normal file
1
changelog.d/4026.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Fix call screen stuck with some hanging up scenarios
|
1
changelog.d/4028.bugfix
Normal file
1
changelog.d/4028.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Fix other call not always refreshed when ended
|
@ -50,7 +50,7 @@ private val loggerTag = LoggerTag("CallService", LoggerTag.VOIP)
|
|||||||
class CallService : VectorService() {
|
class CallService : VectorService() {
|
||||||
|
|
||||||
private val connections = mutableMapOf<String, CallConnection>()
|
private val connections = mutableMapOf<String, CallConnection>()
|
||||||
private val knownCalls = mutableSetOf<CallInformation>()
|
private val knownCalls = mutableMapOf<String, CallInformation>()
|
||||||
private val connectedCallIds = mutableSetOf<String>()
|
private val connectedCallIds = mutableSetOf<String>()
|
||||||
|
|
||||||
private lateinit var notificationManager: NotificationManagerCompat
|
private lateinit var notificationManager: NotificationManagerCompat
|
||||||
@ -190,7 +190,7 @@ class CallService : VectorService() {
|
|||||||
} else {
|
} else {
|
||||||
notificationManager.notify(callId.hashCode(), notification)
|
notificationManager.notify(callId.hashCode(), notification)
|
||||||
}
|
}
|
||||||
knownCalls.add(callInformation)
|
knownCalls[callId] = callInformation
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleCallTerminated(intent: Intent) {
|
private fun handleCallTerminated(intent: Intent) {
|
||||||
@ -198,20 +198,22 @@ class CallService : VectorService() {
|
|||||||
val endCallReason = intent.getSerializableExtra(EXTRA_END_CALL_REASON) as EndCallReason
|
val endCallReason = intent.getSerializableExtra(EXTRA_END_CALL_REASON) as EndCallReason
|
||||||
val rejected = intent.getBooleanExtra(EXTRA_END_CALL_REJECTED, false)
|
val rejected = intent.getBooleanExtra(EXTRA_END_CALL_REJECTED, false)
|
||||||
alertManager.cancelAlert(callId)
|
alertManager.cancelAlert(callId)
|
||||||
val terminatedCall = knownCalls.firstOrNull { it.callId == callId }
|
val terminatedCall = knownCalls.remove(callId)
|
||||||
if (terminatedCall == null) {
|
if (terminatedCall == null) {
|
||||||
Timber.tag(loggerTag.value).v("Call terminated for unknown call $callId$")
|
Timber.tag(loggerTag.value).v("Call terminated for unknown call $callId")
|
||||||
handleUnexpectedState(callId)
|
handleUnexpectedState(callId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
knownCalls.remove(terminatedCall)
|
val notification = notificationUtils.buildCallEndedNotification(false)
|
||||||
|
val notificationId = callId.hashCode()
|
||||||
|
startForeground(notificationId, notification)
|
||||||
if (knownCalls.isEmpty()) {
|
if (knownCalls.isEmpty()) {
|
||||||
|
Timber.tag(loggerTag.value).v("No more call, stop the service")
|
||||||
|
stopForeground(true)
|
||||||
mediaSession?.isActive = false
|
mediaSession?.isActive = false
|
||||||
myStopSelf()
|
myStopSelf()
|
||||||
}
|
}
|
||||||
val wasConnected = connectedCallIds.remove(callId)
|
val wasConnected = connectedCallIds.remove(callId)
|
||||||
val notification = notificationUtils.buildCallEndedNotification(terminatedCall.isVideoCall)
|
|
||||||
notificationManager.notify(callId.hashCode(), notification)
|
|
||||||
if (!wasConnected && !terminatedCall.isOutgoing && !rejected && endCallReason != EndCallReason.ANSWERED_ELSEWHERE) {
|
if (!wasConnected && !terminatedCall.isOutgoing && !rejected && endCallReason != EndCallReason.ANSWERED_ELSEWHERE) {
|
||||||
val missedCallNotification = notificationUtils.buildCallMissedNotification(terminatedCall)
|
val missedCallNotification = notificationUtils.buildCallMissedNotification(terminatedCall)
|
||||||
notificationManager.notify(MISSED_CALL_TAG, terminatedCall.nativeRoomId.hashCode(), missedCallNotification)
|
notificationManager.notify(MISSED_CALL_TAG, terminatedCall.nativeRoomId.hashCode(), missedCallNotification)
|
||||||
@ -243,7 +245,7 @@ class CallService : VectorService() {
|
|||||||
} else {
|
} else {
|
||||||
notificationManager.notify(callId.hashCode(), notification)
|
notificationManager.notify(callId.hashCode(), notification)
|
||||||
}
|
}
|
||||||
knownCalls.add(callInformation)
|
knownCalls[callId] = callInformation
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -267,18 +269,19 @@ class CallService : VectorService() {
|
|||||||
} else {
|
} else {
|
||||||
notificationManager.notify(callId.hashCode(), notification)
|
notificationManager.notify(callId.hashCode(), notification)
|
||||||
}
|
}
|
||||||
knownCalls.add(callInformation)
|
knownCalls[callId] = callInformation
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleUnexpectedState(callId: String?) {
|
private fun handleUnexpectedState(callId: String?) {
|
||||||
Timber.tag(loggerTag.value).v("Fallback to clear everything")
|
Timber.tag(loggerTag.value).v("Fallback to clear everything")
|
||||||
callRingPlayerIncoming?.stop()
|
callRingPlayerIncoming?.stop()
|
||||||
callRingPlayerOutgoing?.stop()
|
callRingPlayerOutgoing?.stop()
|
||||||
if (callId != null) {
|
|
||||||
notificationManager.cancel(callId.hashCode())
|
|
||||||
}
|
|
||||||
val notification = notificationUtils.buildCallEndedNotification(false)
|
val notification = notificationUtils.buildCallEndedNotification(false)
|
||||||
startForeground(DEFAULT_NOTIFICATION_ID, notification)
|
if (callId != null) {
|
||||||
|
startForeground(callId.hashCode(), notification)
|
||||||
|
} else {
|
||||||
|
startForeground(DEFAULT_NOTIFICATION_ID, notification)
|
||||||
|
}
|
||||||
if (knownCalls.isEmpty()) {
|
if (knownCalls.isEmpty()) {
|
||||||
mediaSession?.isActive = false
|
mediaSession?.isActive = false
|
||||||
myStopSelf()
|
myStopSelf()
|
||||||
@ -371,7 +374,7 @@ class CallService : VectorService() {
|
|||||||
putExtra(EXTRA_END_CALL_REASON, endCallReason)
|
putExtra(EXTRA_END_CALL_REASON, endCallReason)
|
||||||
putExtra(EXTRA_END_CALL_REJECTED, rejected)
|
putExtra(EXTRA_END_CALL_REJECTED, rejected)
|
||||||
}
|
}
|
||||||
ContextCompat.startForegroundService(context, intent)
|
context.startService(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class CurrentCallsViewPresenter {
|
|||||||
this.currentCall = currentCall
|
this.currentCall = currentCall
|
||||||
this.currentCall?.addListener(tickListener)
|
this.currentCall?.addListener(tickListener)
|
||||||
this.calls = calls
|
this.calls = calls
|
||||||
val hasActiveCall = currentCall != null
|
val hasActiveCall = calls.isNotEmpty()
|
||||||
currentCallsView?.isVisible = hasActiveCall
|
currentCallsView?.isVisible = hasActiveCall
|
||||||
currentCallsView?.render(calls, currentCall?.formattedDuration() ?: "")
|
currentCallsView?.render(calls, currentCall?.formattedDuration() ?: "")
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ class SharedKnownCallsViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val currentCallListener = object : WebRtcCallManager.CurrentCallListener {
|
private val callManagerListener = object : WebRtcCallManager.Listener {
|
||||||
override fun onCurrentCallChange(call: WebRtcCall?) {
|
override fun onCurrentCallChange(call: WebRtcCall?) {
|
||||||
val knownCalls = callManager.getCalls()
|
val knownCalls = callManager.getCalls()
|
||||||
liveKnownCalls.postValue(knownCalls)
|
liveKnownCalls.postValue(knownCalls)
|
||||||
@ -50,12 +50,17 @@ class SharedKnownCallsViewModel @Inject constructor(
|
|||||||
it.addListener(callListener)
|
it.addListener(callListener)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCallEnded(callId: String) {
|
||||||
|
val knownCalls = callManager.getCalls()
|
||||||
|
liveKnownCalls.postValue(knownCalls)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val knownCalls = callManager.getCalls()
|
val knownCalls = callManager.getCalls()
|
||||||
liveKnownCalls.postValue(knownCalls)
|
liveKnownCalls.postValue(knownCalls)
|
||||||
callManager.addCurrentCallListener(currentCallListener)
|
callManager.addListener(callManagerListener)
|
||||||
knownCalls.forEach {
|
knownCalls.forEach {
|
||||||
it.addListener(callListener)
|
it.addListener(callListener)
|
||||||
}
|
}
|
||||||
@ -65,7 +70,7 @@ class SharedKnownCallsViewModel @Inject constructor(
|
|||||||
callManager.getCalls().forEach {
|
callManager.getCalls().forEach {
|
||||||
it.removeListener(callListener)
|
it.removeListener(callListener)
|
||||||
}
|
}
|
||||||
callManager.removeCurrentCallListener(currentCallListener)
|
callManager.removeListener(callManagerListener)
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,15 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||||||
} ?: VectorCallViewState.TransfereeState.UnknownTransferee
|
} ?: VectorCallViewState.TransfereeState.UnknownTransferee
|
||||||
}
|
}
|
||||||
|
|
||||||
private val currentCallListener = object : WebRtcCallManager.CurrentCallListener {
|
private val callManagerListener = object : WebRtcCallManager.Listener {
|
||||||
|
|
||||||
|
override fun onCallEnded(callId: String) {
|
||||||
|
withState { state ->
|
||||||
|
if (state.otherKnownCallInfo?.callId == callId) {
|
||||||
|
setState { copy(otherKnownCallInfo = null) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCurrentCallChange(call: WebRtcCall?) {
|
override fun onCurrentCallChange(call: WebRtcCall?) {
|
||||||
if (call != null) {
|
if (call != null) {
|
||||||
@ -159,9 +167,7 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateOtherKnownCall(currentCall: WebRtcCall) {
|
private fun updateOtherKnownCall(currentCall: WebRtcCall) {
|
||||||
val otherCall = callManager.getCalls().firstOrNull {
|
val otherCall = getOtherKnownCall(currentCall)
|
||||||
it.callId != currentCall.callId && it.mxCall.state is CallState.Connected
|
|
||||||
}
|
|
||||||
setState {
|
setState {
|
||||||
if (otherCall == null) {
|
if (otherCall == null) {
|
||||||
copy(otherKnownCallInfo = null)
|
copy(otherKnownCallInfo = null)
|
||||||
@ -171,6 +177,12 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getOtherKnownCall(currentCall: WebRtcCall): WebRtcCall? {
|
||||||
|
return callManager.getCalls().firstOrNull {
|
||||||
|
it.callId != currentCall.callId && it.mxCall.state is CallState.Connected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setupCallWithCurrentState()
|
setupCallWithCurrentState()
|
||||||
}
|
}
|
||||||
@ -184,7 +196,7 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
call = webRtcCall
|
call = webRtcCall
|
||||||
callManager.addCurrentCallListener(currentCallListener)
|
callManager.addListener(callManagerListener)
|
||||||
webRtcCall.addListener(callListener)
|
webRtcCall.addListener(callListener)
|
||||||
val currentSoundDevice = callManager.audioManager.selectedDevice
|
val currentSoundDevice = callManager.audioManager.selectedDevice
|
||||||
if (currentSoundDevice == CallAudioManager.Device.Phone) {
|
if (currentSoundDevice == CallAudioManager.Device.Phone) {
|
||||||
@ -230,7 +242,7 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
callManager.removeCurrentCallListener(currentCallListener)
|
callManager.removeListener(callManagerListener)
|
||||||
call?.removeListener(callListener)
|
call?.removeListener(callListener)
|
||||||
call = null
|
call = null
|
||||||
proximityManager.stop()
|
proximityManager.stop()
|
||||||
@ -310,10 +322,10 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||||||
VectorCallViewEvents.ShowCallTransferScreen
|
VectorCallViewEvents.ShowCallTransferScreen
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
VectorCallViewActions.TransferCall -> {
|
VectorCallViewActions.TransferCall -> {
|
||||||
handleCallTransfer()
|
handleCallTransfer()
|
||||||
}
|
}
|
||||||
is VectorCallViewActions.SwitchCall -> {
|
is VectorCallViewActions.SwitchCall -> {
|
||||||
setState { VectorCallViewState(action.callArgs) }
|
setState { VectorCallViewState(action.callArgs) }
|
||||||
setupCallWithCurrentState()
|
setupCallWithCurrentState()
|
||||||
}
|
}
|
||||||
|
@ -810,17 +810,19 @@ class WebRtcCall(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun endCall(reason: EndCallReason = EndCallReason.USER_HANGUP) {
|
fun endCall(reason: EndCallReason = EndCallReason.USER_HANGUP, sendSignaling: Boolean = true) {
|
||||||
sessionScope?.launch(dispatcher) {
|
sessionScope?.launch(dispatcher) {
|
||||||
if (mxCall.state is CallState.Ended) {
|
if (mxCall.state is CallState.Ended) {
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
val reject = mxCall.state is CallState.LocalRinging
|
val reject = mxCall.state is CallState.LocalRinging
|
||||||
terminate(reason, reject)
|
terminate(reason, reject)
|
||||||
if (reject) {
|
if (sendSignaling) {
|
||||||
mxCall.reject()
|
if (reject) {
|
||||||
} else {
|
mxCall.reject()
|
||||||
mxCall.hangUp(reason)
|
} else {
|
||||||
|
mxCall.hangUp(reason)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,9 +84,10 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
private val sessionScope: CoroutineScope?
|
private val sessionScope: CoroutineScope?
|
||||||
get() = currentSession?.coroutineScope
|
get() = currentSession?.coroutineScope
|
||||||
|
|
||||||
interface CurrentCallListener {
|
interface Listener {
|
||||||
fun onCurrentCallChange(call: WebRtcCall?) {}
|
fun onCallEnded(callId: String) = Unit
|
||||||
fun onAudioDevicesChange() {}
|
fun onCurrentCallChange(call: WebRtcCall?) = Unit
|
||||||
|
fun onAudioDevicesChange() = Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
val supportedPSTNProtocol: String?
|
val supportedPSTNProtocol: String?
|
||||||
@ -106,13 +107,13 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
protocolsChecker?.removeListener(listener)
|
protocolsChecker?.removeListener(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val currentCallsListeners = CopyOnWriteArrayList<CurrentCallListener>()
|
private val currentCallsListeners = CopyOnWriteArrayList<Listener>()
|
||||||
|
|
||||||
fun addCurrentCallListener(listener: CurrentCallListener) {
|
fun addListener(listener: Listener) {
|
||||||
currentCallsListeners.add(listener)
|
currentCallsListeners.add(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeCurrentCallListener(listener: CurrentCallListener) {
|
fun removeListener(listener: Listener) {
|
||||||
currentCallsListeners.remove(listener)
|
currentCallsListeners.remove(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,10 +251,13 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
callsByRoomId[webRtcCall.signalingRoomId]?.remove(webRtcCall)
|
callsByRoomId[webRtcCall.signalingRoomId]?.remove(webRtcCall)
|
||||||
callsByRoomId[webRtcCall.nativeRoomId]?.remove(webRtcCall)
|
callsByRoomId[webRtcCall.nativeRoomId]?.remove(webRtcCall)
|
||||||
transferees.remove(callId)
|
transferees.remove(callId)
|
||||||
if (getCurrentCall()?.callId == callId) {
|
if (currentCall.get()?.callId == callId) {
|
||||||
val otherCall = getCalls().lastOrNull()
|
val otherCall = getCalls().lastOrNull()
|
||||||
currentCall.setAndNotify(otherCall)
|
currentCall.setAndNotify(otherCall)
|
||||||
}
|
}
|
||||||
|
tryOrNull {
|
||||||
|
currentCallsListeners.forEach { it.onCallEnded(callId) }
|
||||||
|
}
|
||||||
// There is no active calls
|
// There is no active calls
|
||||||
if (getCurrentCall() == null) {
|
if (getCurrentCall() == null) {
|
||||||
Timber.tag(loggerTag.value).v("Dispose peerConnectionFactory as there is no need to keep one")
|
Timber.tag(loggerTag.value).v("Dispose peerConnectionFactory as there is no need to keep one")
|
||||||
@ -424,7 +428,11 @@ class WebRtcCallManager @Inject constructor(
|
|||||||
|
|
||||||
override fun onCallManagedByOtherSession(callId: String) {
|
override fun onCallManagedByOtherSession(callId: String) {
|
||||||
Timber.tag(loggerTag.value).v("onCallManagedByOtherSession: $callId")
|
Timber.tag(loggerTag.value).v("onCallManagedByOtherSession: $callId")
|
||||||
onCallEnded(callId, EndCallReason.ANSWERED_ELSEWHERE, false)
|
val call = callsByCallId[callId]
|
||||||
|
?: return Unit.also {
|
||||||
|
Timber.tag(loggerTag.value).w("onCallManagedByOtherSession for non active call? $callId")
|
||||||
|
}
|
||||||
|
call.endCall(EndCallReason.ANSWERED_ELSEWHERE, sendSignaling = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) {
|
override fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) {
|
||||||
|
@ -468,7 +468,6 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
|||||||
setSmallIcon(R.drawable.ic_call_answer)
|
setSmallIcon(R.drawable.ic_call_answer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// This is a trick to make the previous notification with same id disappear as cancel notification is not working with Foreground Service.
|
|
||||||
.setTimeoutAfter(1)
|
.setTimeoutAfter(1)
|
||||||
.setColor(ThemeUtils.getColor(context, android.R.attr.colorPrimary))
|
.setColor(ThemeUtils.getColor(context, android.R.attr.colorPrimary))
|
||||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
.setCategory(NotificationCompat.CATEGORY_CALL)
|
||||||
|
Loading…
Reference in New Issue
Block a user