Merge pull request #4029 from vector-im/feature/fga/fix_voip_issues

Feature/fga/fix voip issues
This commit is contained in:
Benoit Marty 2021-09-24 09:43:39 +02:00 committed by GitHub
commit 863ba609df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 72 additions and 40 deletions

1
changelog.d/4019.bugfix Normal file
View file

@ -0,0 +1 @@
Fix sticky end call notification

1
changelog.d/4026.bugfix Normal file
View file

@ -0,0 +1 @@
Fix call screen stuck with some hanging up scenarios

1
changelog.d/4028.bugfix Normal file
View file

@ -0,0 +1 @@
Fix other call not always refreshed when ended

View file

@ -50,7 +50,7 @@ private val loggerTag = LoggerTag("CallService", LoggerTag.VOIP)
class CallService : VectorService() {
private val connections = mutableMapOf<String, CallConnection>()
private val knownCalls = mutableSetOf<CallInformation>()
private val knownCalls = mutableMapOf<String, CallInformation>()
private val connectedCallIds = mutableSetOf<String>()
private lateinit var notificationManager: NotificationManagerCompat
@ -190,7 +190,7 @@ class CallService : VectorService() {
} else {
notificationManager.notify(callId.hashCode(), notification)
}
knownCalls.add(callInformation)
knownCalls[callId] = callInformation
}
private fun handleCallTerminated(intent: Intent) {
@ -198,20 +198,22 @@ class CallService : VectorService() {
val endCallReason = intent.getSerializableExtra(EXTRA_END_CALL_REASON) as EndCallReason
val rejected = intent.getBooleanExtra(EXTRA_END_CALL_REJECTED, false)
alertManager.cancelAlert(callId)
val terminatedCall = knownCalls.firstOrNull { it.callId == callId }
val terminatedCall = knownCalls.remove(callId)
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)
return
}
knownCalls.remove(terminatedCall)
val notification = notificationUtils.buildCallEndedNotification(false)
val notificationId = callId.hashCode()
startForeground(notificationId, notification)
if (knownCalls.isEmpty()) {
Timber.tag(loggerTag.value).v("No more call, stop the service")
stopForeground(true)
mediaSession?.isActive = false
myStopSelf()
}
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) {
val missedCallNotification = notificationUtils.buildCallMissedNotification(terminatedCall)
notificationManager.notify(MISSED_CALL_TAG, terminatedCall.nativeRoomId.hashCode(), missedCallNotification)
@ -243,7 +245,7 @@ class CallService : VectorService() {
} else {
notificationManager.notify(callId.hashCode(), notification)
}
knownCalls.add(callInformation)
knownCalls[callId] = callInformation
}
/**
@ -267,18 +269,19 @@ class CallService : VectorService() {
} else {
notificationManager.notify(callId.hashCode(), notification)
}
knownCalls.add(callInformation)
knownCalls[callId] = callInformation
}
private fun handleUnexpectedState(callId: String?) {
Timber.tag(loggerTag.value).v("Fallback to clear everything")
callRingPlayerIncoming?.stop()
callRingPlayerOutgoing?.stop()
if (callId != null) {
notificationManager.cancel(callId.hashCode())
}
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()) {
mediaSession?.isActive = false
myStopSelf()
@ -371,7 +374,7 @@ class CallService : VectorService() {
putExtra(EXTRA_END_CALL_REASON, endCallReason)
putExtra(EXTRA_END_CALL_REJECTED, rejected)
}
ContextCompat.startForegroundService(context, intent)
context.startService(intent)
}
}

View file

@ -36,7 +36,7 @@ class CurrentCallsViewPresenter {
this.currentCall = currentCall
this.currentCall?.addListener(tickListener)
this.calls = calls
val hasActiveCall = currentCall != null
val hasActiveCall = calls.isNotEmpty()
currentCallsView?.isVisible = hasActiveCall
currentCallsView?.render(calls, currentCall?.formattedDuration() ?: "")
}

View file

@ -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?) {
val knownCalls = callManager.getCalls()
liveKnownCalls.postValue(knownCalls)
@ -50,12 +50,17 @@ class SharedKnownCallsViewModel @Inject constructor(
it.addListener(callListener)
}
}
override fun onCallEnded(callId: String) {
val knownCalls = callManager.getCalls()
liveKnownCalls.postValue(knownCalls)
}
}
init {
val knownCalls = callManager.getCalls()
liveKnownCalls.postValue(knownCalls)
callManager.addCurrentCallListener(currentCallListener)
callManager.addListener(callManagerListener)
knownCalls.forEach {
it.addListener(callListener)
}
@ -65,7 +70,7 @@ class SharedKnownCallsViewModel @Inject constructor(
callManager.getCalls().forEach {
it.removeListener(callListener)
}
callManager.removeCurrentCallListener(currentCallListener)
callManager.removeListener(callManagerListener)
super.onCleared()
}
}

View file

@ -134,7 +134,15 @@ class VectorCallViewModel @AssistedInject constructor(
} ?: 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?) {
if (call != null) {
@ -159,9 +167,7 @@ class VectorCallViewModel @AssistedInject constructor(
}
private fun updateOtherKnownCall(currentCall: WebRtcCall) {
val otherCall = callManager.getCalls().firstOrNull {
it.callId != currentCall.callId && it.mxCall.state is CallState.Connected
}
val otherCall = getOtherKnownCall(currentCall)
setState {
if (otherCall == 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 {
setupCallWithCurrentState()
}
@ -184,7 +196,7 @@ class VectorCallViewModel @AssistedInject constructor(
}
} else {
call = webRtcCall
callManager.addCurrentCallListener(currentCallListener)
callManager.addListener(callManagerListener)
webRtcCall.addListener(callListener)
val currentSoundDevice = callManager.audioManager.selectedDevice
if (currentSoundDevice == CallAudioManager.Device.Phone) {
@ -230,7 +242,7 @@ class VectorCallViewModel @AssistedInject constructor(
}
override fun onCleared() {
callManager.removeCurrentCallListener(currentCallListener)
callManager.removeListener(callManagerListener)
call?.removeListener(callListener)
call = null
proximityManager.stop()
@ -310,10 +322,10 @@ class VectorCallViewModel @AssistedInject constructor(
VectorCallViewEvents.ShowCallTransferScreen
)
}
VectorCallViewActions.TransferCall -> {
VectorCallViewActions.TransferCall -> {
handleCallTransfer()
}
is VectorCallViewActions.SwitchCall -> {
is VectorCallViewActions.SwitchCall -> {
setState { VectorCallViewState(action.callArgs) }
setupCallWithCurrentState()
}

View file

@ -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) {
if (mxCall.state is CallState.Ended) {
return@launch
}
val reject = mxCall.state is CallState.LocalRinging
terminate(reason, reject)
if (reject) {
mxCall.reject()
} else {
mxCall.hangUp(reason)
if (sendSignaling) {
if (reject) {
mxCall.reject()
} else {
mxCall.hangUp(reason)
}
}
}
}

View file

@ -84,9 +84,10 @@ class WebRtcCallManager @Inject constructor(
private val sessionScope: CoroutineScope?
get() = currentSession?.coroutineScope
interface CurrentCallListener {
fun onCurrentCallChange(call: WebRtcCall?) {}
fun onAudioDevicesChange() {}
interface Listener {
fun onCallEnded(callId: String) = Unit
fun onCurrentCallChange(call: WebRtcCall?) = Unit
fun onAudioDevicesChange() = Unit
}
val supportedPSTNProtocol: String?
@ -106,13 +107,13 @@ class WebRtcCallManager @Inject constructor(
protocolsChecker?.removeListener(listener)
}
private val currentCallsListeners = CopyOnWriteArrayList<CurrentCallListener>()
private val currentCallsListeners = CopyOnWriteArrayList<Listener>()
fun addCurrentCallListener(listener: CurrentCallListener) {
fun addListener(listener: Listener) {
currentCallsListeners.add(listener)
}
fun removeCurrentCallListener(listener: CurrentCallListener) {
fun removeListener(listener: Listener) {
currentCallsListeners.remove(listener)
}
@ -250,10 +251,13 @@ class WebRtcCallManager @Inject constructor(
callsByRoomId[webRtcCall.signalingRoomId]?.remove(webRtcCall)
callsByRoomId[webRtcCall.nativeRoomId]?.remove(webRtcCall)
transferees.remove(callId)
if (getCurrentCall()?.callId == callId) {
if (currentCall.get()?.callId == callId) {
val otherCall = getCalls().lastOrNull()
currentCall.setAndNotify(otherCall)
}
tryOrNull {
currentCallsListeners.forEach { it.onCallEnded(callId) }
}
// There is no active calls
if (getCurrentCall() == null) {
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) {
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) {

View file

@ -468,7 +468,6 @@ class NotificationUtils @Inject constructor(private val context: Context,
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)
.setColor(ThemeUtils.getColor(context, android.R.attr.colorPrimary))
.setCategory(NotificationCompat.CATEGORY_CALL)