mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 08:15:24 +00:00
feat(healthchat): ring calls via gateway VoIP push
CallKit rejects the NSE hand-off without the filtering entitlement.
This commit is contained in:
@@ -256,13 +256,62 @@ nonisolated class NotificationHandler {
|
||||
try await CXProvider.reportNewIncomingVoIPPushPayload(payload)
|
||||
MXLog.info("Call notification delegated to CallKit")
|
||||
} catch {
|
||||
MXLog.error("Failed reporting voip call with error: \(error). Handling as push notification")
|
||||
// HealthChat: without the notification filtering entitlement CallKit rejects the hand-off,
|
||||
// so ask the push gateway for a PushKit push instead and keep the alert visible.
|
||||
MXLog.error("Failed reporting voip call with error: \(error). Requesting a VoIP push instead")
|
||||
await requestVoIPPush(roomID: roomID,
|
||||
roomDisplayName: roomDisplayName,
|
||||
rtcNotifyEventID: rtcNotifyEventID,
|
||||
expirationDate: expirationDate,
|
||||
isVoiceCall: callIntent == RtcCallIntent.audio)
|
||||
return .shouldDisplay
|
||||
}
|
||||
|
||||
return .processedShouldDiscard
|
||||
}
|
||||
|
||||
/// Asks the push gateway to deliver a PushKit push with the call details, the way the legacy
|
||||
/// element-ios app did. The app's `PKPushRegistryDelegate` then reports the call to CallKit.
|
||||
private func requestVoIPPush(roomID: String, roomDisplayName: String, rtcNotifyEventID: String, expirationDate: Date, isVoiceCall: Bool) async {
|
||||
guard let token = settings.pushKitDeviceToken,
|
||||
let notifyURL = settings.voipPushGatewayNotifyURL.flatMap(URL.init(string:)) else {
|
||||
MXLog.warning("No PushKit token or push gateway stored by the app, cannot request a VoIP push")
|
||||
return
|
||||
}
|
||||
|
||||
// The gateway merges `default_payload` into the push verbatim, so the keys match what the app expects.
|
||||
let defaultPayload: [String: Any] = [ElementCallServiceNotificationKey.roomID.rawValue: roomID,
|
||||
ElementCallServiceNotificationKey.roomDisplayName.rawValue: roomDisplayName,
|
||||
ElementCallServiceNotificationKey.rtcNotifyEventID.rawValue: rtcNotifyEventID,
|
||||
ElementCallServiceNotificationKey.expirationDate.rawValue: expirationDate.timeIntervalSince1970,
|
||||
ElementCallServiceNotificationKey.isVoiceCall.rawValue: isVoiceCall]
|
||||
let body: [String: Any] = ["notification": ["event_id": rtcNotifyEventID,
|
||||
"room_id": roomID,
|
||||
"prio": "high",
|
||||
"devices": [["app_id": settings.voipPusherAppID,
|
||||
"pushkey": token,
|
||||
"data": ["format": "event_id_only",
|
||||
"default_payload": defaultPayload]]]]]
|
||||
|
||||
var request = URLRequest(url: notifyURL, timeoutInterval: 5)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
do {
|
||||
request.httpBody = try JSONSerialization.data(withJSONObject: body)
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
let statusCode = (response as? HTTPURLResponse)?.statusCode ?? 0
|
||||
let rejected = (try? JSONSerialization.jsonObject(with: data) as? [String: Any])?["rejected"] as? [String] ?? []
|
||||
guard statusCode == 200, rejected.isEmpty else {
|
||||
MXLog.error("Push gateway refused the VoIP push: status \(statusCode), rejected \(rejected.count) pushkey(s)")
|
||||
return
|
||||
}
|
||||
MXLog.info("Requested a VoIP push for the call in \(roomID)")
|
||||
} catch {
|
||||
MXLog.error("Failed requesting a VoIP push: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private enum NotificationProcessingResult {
|
||||
case shouldDisplay
|
||||
case processedShouldDiscard
|
||||
|
||||
Reference in New Issue
Block a user