Listen to call decline to stop ringing when declined from other device (#4505)
* Listen to call decline to stop ringing when declined from other device * use proper swift naming convention for Id/ID * fix Force unwrapping * fix lint * An approach without the custom publisher. * review: correct naming convention * review: revert some invisible char/tab changes * add ref to the room proxy in the closure --------- Co-authored-by: Doug <douglase@element.io>
This commit is contained in:
@@ -9067,6 +9067,76 @@ class JoinedRoomProxyMock: JoinedRoomProxyProtocol, @unchecked Sendable {
|
||||
return declineCallNotificationIDReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - subscribeToCallDeclineEvents
|
||||
|
||||
var subscribeToCallDeclineEventsRtcNotificationEventIDListenerUnderlyingCallsCount = 0
|
||||
var subscribeToCallDeclineEventsRtcNotificationEventIDListenerCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return subscribeToCallDeclineEventsRtcNotificationEventIDListenerUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = subscribeToCallDeclineEventsRtcNotificationEventIDListenerUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
subscribeToCallDeclineEventsRtcNotificationEventIDListenerUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
subscribeToCallDeclineEventsRtcNotificationEventIDListenerUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var subscribeToCallDeclineEventsRtcNotificationEventIDListenerCalled: Bool {
|
||||
return subscribeToCallDeclineEventsRtcNotificationEventIDListenerCallsCount > 0
|
||||
}
|
||||
var subscribeToCallDeclineEventsRtcNotificationEventIDListenerReceivedArguments: (rtcNotificationEventID: String, listener: CallDeclineListener)?
|
||||
var subscribeToCallDeclineEventsRtcNotificationEventIDListenerReceivedInvocations: [(rtcNotificationEventID: String, listener: CallDeclineListener)] = []
|
||||
|
||||
var subscribeToCallDeclineEventsRtcNotificationEventIDListenerUnderlyingReturnValue: Result<TaskHandle, RoomProxyError>!
|
||||
var subscribeToCallDeclineEventsRtcNotificationEventIDListenerReturnValue: Result<TaskHandle, RoomProxyError>! {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return subscribeToCallDeclineEventsRtcNotificationEventIDListenerUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: Result<TaskHandle, RoomProxyError>? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = subscribeToCallDeclineEventsRtcNotificationEventIDListenerUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
subscribeToCallDeclineEventsRtcNotificationEventIDListenerUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
subscribeToCallDeclineEventsRtcNotificationEventIDListenerUnderlyingReturnValue = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var subscribeToCallDeclineEventsRtcNotificationEventIDListenerClosure: ((String, CallDeclineListener) -> Result<TaskHandle, RoomProxyError>)?
|
||||
|
||||
func subscribeToCallDeclineEvents(rtcNotificationEventID: String, listener: CallDeclineListener) -> Result<TaskHandle, RoomProxyError> {
|
||||
subscribeToCallDeclineEventsRtcNotificationEventIDListenerCallsCount += 1
|
||||
subscribeToCallDeclineEventsRtcNotificationEventIDListenerReceivedArguments = (rtcNotificationEventID: rtcNotificationEventID, listener: listener)
|
||||
DispatchQueue.main.async {
|
||||
self.subscribeToCallDeclineEventsRtcNotificationEventIDListenerReceivedInvocations.append((rtcNotificationEventID: rtcNotificationEventID, listener: listener))
|
||||
}
|
||||
if let subscribeToCallDeclineEventsRtcNotificationEventIDListenerClosure = subscribeToCallDeclineEventsRtcNotificationEventIDListenerClosure {
|
||||
return subscribeToCallDeclineEventsRtcNotificationEventIDListenerClosure(rtcNotificationEventID, listener)
|
||||
} else {
|
||||
return subscribeToCallDeclineEventsRtcNotificationEventIDListenerReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - matrixToPermalink
|
||||
|
||||
var matrixToPermalinkUnderlyingCallsCount = 0
|
||||
|
||||
@@ -98,6 +98,10 @@ extension SDKListener: RoomInfoListener where T == RoomInfo {
|
||||
func call(roomInfo: RoomInfo) { onUpdateClosure(roomInfo) }
|
||||
}
|
||||
|
||||
extension SDKListener: CallDeclineListener where T == String {
|
||||
func call(declinerUserId: String) { onUpdateClosure(declinerUserId) }
|
||||
}
|
||||
|
||||
// MARK: TimelineProxy
|
||||
|
||||
extension SDKListener: PaginationStatusListener where T == RoomPaginationStatus {
|
||||
|
||||
@@ -9,6 +9,7 @@ import AVFoundation
|
||||
import CallKit
|
||||
import Combine
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
import PushKit
|
||||
import UIKit
|
||||
|
||||
@@ -41,14 +42,14 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
|
||||
// There's a race condition where a call starts when the app has been killed and the
|
||||
// observation set in `incomingCallID` occurs *before* the user session is restored.
|
||||
// So observe when the client proxy is set to fix this (the method guards for the call).
|
||||
Task { await observeIncomingCallRoomInfo() }
|
||||
Task { await observeIncomingCall() }
|
||||
}
|
||||
}
|
||||
|
||||
private var incomingCallRoomInfoCancellable: AnyCancellable?
|
||||
private var incomingCallID: CallID? {
|
||||
didSet {
|
||||
Task { await observeIncomingCallRoomInfo() }
|
||||
Task { await observeIncomingCall() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +69,8 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
private var declineListenerHandle: TaskHandle?
|
||||
|
||||
override init() {
|
||||
pushRegistry = PKPushRegistry(queue: nil)
|
||||
|
||||
@@ -305,7 +308,7 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
|
||||
_ = await roomProxy.declineCall(notificationID: rtcNotificationID)
|
||||
}
|
||||
|
||||
private func observeIncomingCallRoomInfo() async {
|
||||
private func observeIncomingCall() async {
|
||||
incomingCallRoomInfoCancellable = nil
|
||||
|
||||
guard let incomingCallID else {
|
||||
@@ -341,17 +344,44 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
|
||||
|
||||
if !hasOngoingCall {
|
||||
MXLog.info("Call cancelled by remote")
|
||||
|
||||
incomingCallRoomInfoCancellable = nil
|
||||
endUnansweredCallTask?.cancel()
|
||||
callProvider.reportCall(with: incomingCallID.callKitID, endedAt: nil, reason: .remoteEnded)
|
||||
reportEndedCall(incomingCallID: incomingCallID, reason: .remoteEnded)
|
||||
} else if participants.contains(roomProxy.ownUserID) {
|
||||
MXLog.info("Call answered elsewhere")
|
||||
|
||||
incomingCallRoomInfoCancellable = nil
|
||||
endUnansweredCallTask?.cancel()
|
||||
callProvider.reportCall(with: incomingCallID.callKitID, endedAt: nil, reason: .answeredElsewhere)
|
||||
reportEndedCall(incomingCallID: incomingCallID, reason: .answeredElsewhere)
|
||||
}
|
||||
}
|
||||
|
||||
guard let rtcNotificationID = incomingCallID.rtcNotificationID else {
|
||||
MXLog.warning("Decline: No RTC notification ID found for the incoming call.")
|
||||
return
|
||||
}
|
||||
|
||||
MXLog.info("Observe decline events for notification \(rtcNotificationID)")
|
||||
|
||||
let listener: CallDeclineListener = SDKListener { [weak self] senderID in
|
||||
guard let self else { return }
|
||||
|
||||
MXLog.debug("Call declined event received from \(senderID)")
|
||||
|
||||
if senderID == roomProxy.ownUserID {
|
||||
// Stop ringing!
|
||||
MXLog.debug("Call declined elsewhere")
|
||||
reportEndedCall(incomingCallID: incomingCallID, reason: .declinedElsewhere)
|
||||
}
|
||||
}
|
||||
|
||||
guard case let .success(handle) = roomProxy.subscribeToCallDeclineEvents(rtcNotificationEventID: rtcNotificationID, listener: listener) else {
|
||||
MXLog.error("Unable to listen for decline events.")
|
||||
return
|
||||
}
|
||||
|
||||
declineListenerHandle = handle
|
||||
}
|
||||
|
||||
private func reportEndedCall(incomingCallID: CallID, reason: CXCallEndedReason) {
|
||||
declineListenerHandle?.cancel()
|
||||
declineListenerHandle = nil
|
||||
endUnansweredCallTask?.cancel()
|
||||
callProvider.reportCall(with: incomingCallID.callKitID, endedAt: nil, reason: reason)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,6 +649,17 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
/// Subscribe to call decline events from that rtc notification event.
|
||||
func subscribeToCallDeclineEvents(rtcNotificationEventID: String, listener: CallDeclineListener) -> Result<TaskHandle, RoomProxyError> {
|
||||
do {
|
||||
let handle = try room.subscribeToCallDeclineEvents(rtcNotificationEventId: rtcNotificationEventID, listener: listener)
|
||||
return .success(handle)
|
||||
} catch {
|
||||
MXLog.error("Failed observing rtc decline with error: \(error)")
|
||||
return .failure(.sdkError(error))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Permalinks
|
||||
|
||||
func matrixToPermalink() async -> Result<URL, RoomProxyError> {
|
||||
|
||||
@@ -64,6 +64,13 @@ enum KnockRequestsState {
|
||||
case loaded([KnockRequestProxyProtocol])
|
||||
}
|
||||
|
||||
struct RTCDeclinedEvent {
|
||||
/// The sender of the decline event
|
||||
let sender: String
|
||||
/// The rtc.notification event that is beeing declined
|
||||
let notificationEventID: String
|
||||
}
|
||||
|
||||
// sourcery: AutoMockable
|
||||
protocol JoinedRoomProxyProtocol: RoomProxyProtocol {
|
||||
var infoPublisher: CurrentValuePublisher<RoomInfoProxyProtocol, Never> { get }
|
||||
@@ -170,6 +177,7 @@ protocol JoinedRoomProxyProtocol: RoomProxyProtocol {
|
||||
|
||||
func elementCallWidgetDriver(deviceID: String) -> ElementCallWidgetDriverProtocol
|
||||
func declineCall(notificationID: String) async -> Result<Void, RoomProxyError>
|
||||
func subscribeToCallDeclineEvents(rtcNotificationEventID: String, listener: CallDeclineListener) -> Result<TaskHandle, RoomProxyError>
|
||||
|
||||
// MARK: - Permalinks
|
||||
|
||||
|
||||
Reference in New Issue
Block a user