From bd9a18759751697c55526d07b60f34a00b35ba72 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 16 Sep 2021 10:27:49 +0200 Subject: [PATCH 1/3] Fix sticky end call notification #4019 --- changelog.d/4019.bugfix | 1 + .../vector/app/core/services/CallService.kt | 29 ++++++++++--------- .../notifications/NotificationUtils.kt | 1 - 3 files changed, 17 insertions(+), 14 deletions(-) create mode 100644 changelog.d/4019.bugfix diff --git a/changelog.d/4019.bugfix b/changelog.d/4019.bugfix new file mode 100644 index 00000000000..0bb2e7d8289 --- /dev/null +++ b/changelog.d/4019.bugfix @@ -0,0 +1 @@ +Fix sticky end call notification \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/core/services/CallService.kt b/vector/src/main/java/im/vector/app/core/services/CallService.kt index d8cf8cf6b8a..cd3845f41bb 100644 --- a/vector/src/main/java/im/vector/app/core/services/CallService.kt +++ b/vector/src/main/java/im/vector/app/core/services/CallService.kt @@ -50,7 +50,7 @@ private val loggerTag = LoggerTag("CallService", LoggerTag.VOIP) class CallService : VectorService() { private val connections = mutableMapOf() - private val knownCalls = mutableSetOf() + private val knownCalls = mutableMapOf() private val connectedCallIds = mutableSetOf() 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() + val notification = notificationUtils.buildCallEndedNotification(false) if (callId != null) { - notificationManager.cancel(callId.hashCode()) + startForeground(callId.hashCode(), notification) + } else { + startForeground(DEFAULT_NOTIFICATION_ID, notification) } - val notification = notificationUtils.buildCallEndedNotification(false) - 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) } } diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt index a74c13a4969..fdd4e39665f 100755 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt @@ -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) From 226b0e6c9d0afdee1183d9c18fdb6ada4e2dd155 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 16 Sep 2021 12:04:18 +0200 Subject: [PATCH 2/3] Fix call screen stuck with some hanging up scenarios #4026 --- changelog.d/4026.bugfix | 1 + .../im/vector/app/features/call/webrtc/WebRtcCall.kt | 12 +++++++----- .../app/features/call/webrtc/WebRtcCallManager.kt | 6 +++++- 3 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 changelog.d/4026.bugfix diff --git a/changelog.d/4026.bugfix b/changelog.d/4026.bugfix new file mode 100644 index 00000000000..7404bfe9664 --- /dev/null +++ b/changelog.d/4026.bugfix @@ -0,0 +1 @@ +Fix call screen stuck with some hanging up scenarios \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt index 2d39fda2e30..56368b81754 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt @@ -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) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt index 73a6c07d6af..d276549765f 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt @@ -424,7 +424,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) { From 82864b2b986a95bbfbee17a4f5c390f607af2c24 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 16 Sep 2021 18:53:41 +0200 Subject: [PATCH 3/3] Fix other call not always refreshed when ended #4028 --- changelog.d/4028.bugfix | 1 + .../ui/views/CurrentCallsViewPresenter.kt | 2 +- .../call/SharedKnownCallsViewModel.kt | 11 ++++++-- .../app/features/call/VectorCallViewModel.kt | 28 +++++++++++++------ .../features/call/webrtc/WebRtcCallManager.kt | 18 +++++++----- 5 files changed, 41 insertions(+), 19 deletions(-) create mode 100644 changelog.d/4028.bugfix diff --git a/changelog.d/4028.bugfix b/changelog.d/4028.bugfix new file mode 100644 index 00000000000..f351476b531 --- /dev/null +++ b/changelog.d/4028.bugfix @@ -0,0 +1 @@ +Fix other call not always refreshed when ended \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsViewPresenter.kt b/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsViewPresenter.kt index 5aee73ee694..13db5ddcb3b 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsViewPresenter.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsViewPresenter.kt @@ -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() ?: "") } diff --git a/vector/src/main/java/im/vector/app/features/call/SharedKnownCallsViewModel.kt b/vector/src/main/java/im/vector/app/features/call/SharedKnownCallsViewModel.kt index fb5e48af98a..4b0ea412f38 100644 --- a/vector/src/main/java/im/vector/app/features/call/SharedKnownCallsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/SharedKnownCallsViewModel.kt @@ -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() } } diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt index 63ba83bdbc5..90df595f8ff 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt @@ -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() } diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt index d276549765f..9e620174f30 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt @@ -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() + private val currentCallsListeners = CopyOnWriteArrayList() - 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")