Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Enhancement]Handle video property on VoIP push notification #641

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,12 @@ fileprivate func content() {
callType: .default,
callId: UUID().uuidString,
members: [.init(userId: name)],
ring: true
ring: true,

// hasVideo: A boolean indicating if the call
// will be video or only audio. Still requires
// appropriate setting of ``CallSettings`.`
hasVideo: true
)
}
}
Expand Down
8 changes: 6 additions & 2 deletions Sources/StreamVideo/Call.swift
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ public class Call: @unchecked Sendable, WSEventsSubscriber {
/// - maxDuration: An optional integer representing the maximum duration of the call in seconds.
/// - maxParticipants: An optional integer representing the maximum number of participants allowed in the call.
/// - backstage: An optional backstage request.
/// - hasVideo: A boolean indicating if the call will be video or only audio. Still requires appropriate
/// setting of ``CallSettings`.`
/// - Returns: A `CallResponse` object representing the created call.
/// - Throws: An error if the call creation fails.
@discardableResult
Expand All @@ -238,7 +240,8 @@ public class Call: @unchecked Sendable, WSEventsSubscriber {
notify: Bool = false,
maxDuration: Int? = nil,
maxParticipants: Int? = nil,
backstage: BackstageSettingsRequest? = nil
backstage: BackstageSettingsRequest? = nil,
hasVideo: Bool? = nil
ipavlidakis marked this conversation as resolved.
Show resolved Hide resolved
) async throws -> CallResponse {
var membersRequest = [MemberRequest]()
memberIds?.forEach {
Expand Down Expand Up @@ -268,7 +271,8 @@ public class Call: @unchecked Sendable, WSEventsSubscriber {
members: membersRequest,
settingsOverride: settingsOverride,
startsAt: startsAt,
team: team
team: team,
video: hasVideo
),
notify: notify,
ring: ring
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,26 @@ open class CallKitPushNotificationAdapter: NSObject, PKPushRegistryDelegate, Obs
case displayName = "call_display_name"
case createdByName = "created_by_display_name"
case createdById = "created_by_id"
case video
}

/// Represents the content of a VoIP push notification.
public struct Content {
var cid: String
var localizedCallerName: String
var callerId: String
var hasVideo: Bool?

public init(
cid: String,
localizedCallerName: String,
callerId: String
callerId: String,
hasVideo: Bool? = nil
) {
self.cid = cid
self.localizedCallerName = localizedCallerName
self.callerId = callerId
self.hasVideo = hasVideo
}
}

Expand Down Expand Up @@ -107,6 +111,7 @@ open class CallKitPushNotificationAdapter: NSObject, PKPushRegistryDelegate, Obs
content.cid,
localizedCallerName: content.localizedCallerName,
callerId: content.callerId,
hasVideo: content.hasVideo,
completion: { error in
if let error {
log.error(error)
Expand Down Expand Up @@ -148,10 +153,13 @@ open class CallKitPushNotificationAdapter: NSObject, PKPushRegistryDelegate, Obs
fallback: defaultCallText
)

let hasVideo = streamDict[PayloadKey.video.rawValue] as? Bool

return .init(
cid: cid,
localizedCallerName: localizedCallerName,
callerId: callerId
callerId: callerId,
hasVideo: hasVideo
)
}
}
Expand Down
12 changes: 9 additions & 3 deletions Sources/StreamVideo/CallKit/CallKitService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,22 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
/// - cid: The call ID.
/// - localizedCallerName: The localized caller name.
/// - callerId: The caller's identifier.
/// - hasVideo: Indicator if call is video or audio. If nil we default to the value of `supportsVideo`
/// - completion: A closure to be called upon completion.
@MainActor
open func reportIncomingCall(
ipavlidakis marked this conversation as resolved.
Show resolved Hide resolved
_ cid: String,
localizedCallerName: String,
callerId: String,
hasVideo: Bool? = nil,
completion: @escaping (Error?) -> Void
) {
let hasVideo = hasVideo ?? supportsVideo
let (callUUID, callUpdate) = buildCallUpdate(
cid: cid,
localizedCallerName: localizedCallerName,
callerId: callerId
callerId: callerId,
hasVideo: hasVideo
)

callProvider.reportNewIncomingCall(
Expand All @@ -136,6 +140,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
cid:\(cid)
callerId:\(callerId)
callerName:\(localizedCallerName)
hasVideo: \(hasVideo)
"""
)

Expand Down Expand Up @@ -573,7 +578,8 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
private func buildCallUpdate(
cid: String,
localizedCallerName: String,
callerId: String
callerId: String,
hasVideo: Bool
) -> (UUID, CXCallUpdate) {
let update = CXCallUpdate()
let idComponents = cid.components(separatedBy: ":")
Expand All @@ -589,7 +595,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {

update.localizedCallerName = localizedCallerName
update.remoteHandle = CXHandle(type: .generic, value: callerId)
update.hasVideo = supportsVideo
update.hasVideo = hasVideo
update.supportsDTMF = false

if supportsHolding {
Expand Down
8 changes: 6 additions & 2 deletions Sources/StreamVideoSwiftUI/CallViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ open class CallViewModel: ObservableObject {
/// - maxParticipants: An optional integer representing the maximum number of participants allowed in the call.
/// - startsAt: An optional date when the call starts.
/// - backstage: An optional request for setting up backstage.
/// - hasVideo: A boolean indicating if the call will be video or only audio. Still requires appropriate
ipavlidakis marked this conversation as resolved.
Show resolved Hide resolved
/// setting of ``CallSettings`.`
public func startCall(
callType: String,
callId: String,
Expand All @@ -337,7 +339,8 @@ open class CallViewModel: ObservableObject {
maxParticipants: Int? = nil,
startsAt: Date? = nil,
backstage: BackstageSettingsRequest? = nil,
customData: [String: RawJSON]? = nil
customData: [String: RawJSON]? = nil,
hasVideo: Bool? = nil
) {
outgoingCallMembers = members
callingState = ring ? .outgoing : .joining
Expand Down Expand Up @@ -368,7 +371,8 @@ open class CallViewModel: ObservableObject {
custom: customData,
ring: ring,
maxDuration: maxDuration,
maxParticipants: maxParticipants
maxParticipants: maxParticipants,
hasVideo: hasVideo
)
let timeoutSeconds = TimeInterval(
callData.settings.ring.autoCancelTimeoutMs / 1000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,20 @@ final class CallKitPushNotificationAdapterTests: XCTestCase {
.init(
cid: "123",
localizedCallerName: "TestUser",
callerId: "test_user"
callerId: "test_user",
hasVideo: false
)
)
}

@MainActor
func test_pushRegistryDidReceiveIncomingPush_typeIsVoIP_hasVideoIsNil_reportIncomingCallWasCalledAsExpected() {
assertDidReceivePushNotification(
.init(
cid: "123",
localizedCallerName: "TestUser",
callerId: "test_user",
hasVideo: nil
)
)
}
Expand All @@ -109,7 +122,8 @@ final class CallKitPushNotificationAdapterTests: XCTestCase {
.init(
cid: "123",
localizedCallerName: "TestUser",
callerId: "test_user"
callerId: "test_user",
hasVideo: false
),
displayName: "Stream Group Call"
)
Expand All @@ -132,7 +146,7 @@ final class CallKitPushNotificationAdapterTests: XCTestCase {
) {
let pushPayload = MockPKPushPayload()
pushPayload.stubType = contentType
pushPayload.stubDictionaryPayload = content.map { [
var payload: [String: Any] = content.map { [
"stream": [
"call_cid": $0.cid,
"call_display_name": displayName,
Expand All @@ -141,6 +155,13 @@ final class CallKitPushNotificationAdapterTests: XCTestCase {
]
] } ?? [:]

if let hasVideo = content?.hasVideo, var streamPayload = payload["stream"] as? [String: Any] {
streamPayload["video"] = hasVideo
payload["stream"] = streamPayload
}

pushPayload.stubDictionaryPayload = payload

let completionWasCalledExpectation = expectation(description: "Completion was called.")
completionWasCalledExpectation.isInverted = content == nil
subject.pushRegistry(
Expand Down Expand Up @@ -169,6 +190,12 @@ final class CallKitPushNotificationAdapterTests: XCTestCase {
file: file,
line: line
)
XCTAssertEqual(
callKitService.reportIncomingCallWasCalled?.hasVideo,
content.hasVideo,
file: file,
line: line
)
callKitService.reportIncomingCallWasCalled?.completion(nil)
} else {
XCTAssertNil(
Expand Down
Loading
Loading