From 01d36369852e7a7a7d9982dce4189e5719d40b88 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 11 Nov 2020 13:59:19 +0300 Subject: [PATCH 01/89] Increase call version to 1 --- MatrixSDK/VoIP/MXCall.h | 5 +++++ MatrixSDK/VoIP/MXCall.m | 9 +++++---- MatrixSDKTests/MXVoIPTests.m | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/MatrixSDK/VoIP/MXCall.h b/MatrixSDK/VoIP/MXCall.h index cae47a3cc2..b21564fe6d 100644 --- a/MatrixSDK/VoIP/MXCall.h +++ b/MatrixSDK/VoIP/MXCall.h @@ -72,6 +72,11 @@ typedef NS_ENUM(NSInteger, MXCallEndReason) */ extern NSString *const kMXCallStateDidChange; +/** + Call version + */ +extern NSString *const kMXCallVersion; + @protocol MXCallDelegate; /** diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index b627badaf5..6414b919e1 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -26,6 +26,7 @@ #pragma mark - Constants definitions NSString *const kMXCallStateDidChange = @"kMXCallStateDidChange"; +NSString *const kMXCallVersion = @"1"; @interface MXCall () { @@ -344,7 +345,7 @@ - (void)callWithVideo:(BOOL)video @"type": @"offer", @"sdp": sdp }, - @"version": @(0), + @"version": kMXCallVersion, @"lifetime": @(self->callManager.inviteLifetime) }; [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallInvite content:content localEcho:nil success:^(NSString *eventId) { @@ -406,7 +407,7 @@ - (void)answer @"type": @"answer", @"sdp": sdpAnswer }, - @"version": @(0), + @"version": kMXCallVersion, }; [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallAnswer content:content localEcho:nil success:nil failure:^(NSError *error) { NSLog(@"[MXCall] answer: ERROR: Cannot send m.call.answer event."); @@ -451,7 +452,7 @@ - (void)hangup // Send the hangup event NSDictionary *content = @{ @"call_id": _callId, - @"version": @(0) + @"version": kMXCallVersion }; [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallHangup content:content localEcho:nil success:nil failure:^(NSError *error) { NSLog(@"[MXCall] hangup: ERROR: Cannot send m.call.hangup event."); @@ -636,7 +637,7 @@ - (void)sendLocalIceCandidates NSLog(@"MXCall] onICECandidate: Send %tu candidates", localICECandidates.count); NSDictionary *content = @{ - @"version": @(0), + @"version": kMXCallVersion, @"call_id": _callId, @"candidates": localICECandidates }; diff --git a/MatrixSDKTests/MXVoIPTests.m b/MatrixSDKTests/MXVoIPTests.m index ea9f9ad38f..39324e0246 100644 --- a/MatrixSDKTests/MXVoIPTests.m +++ b/MatrixSDKTests/MXVoIPTests.m @@ -96,7 +96,7 @@ - (void)testNoVoIPStackOnCallInvite @"type": @"offer", @"sdp": @"A SDP" }, - @"version": @(0), + @"version": kMXCallVersion, @"lifetime": @(30 * 1000) }; From 099ccd306ad0a9e18ee2de6bf31bc78872e4bf94 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 11 Nov 2020 15:11:50 +0300 Subject: [PATCH 02/89] Allow version to be a number or a string, fallback to version 1 if not 0 or 1 --- MatrixSDK.xcodeproj/project.pbxproj | 104 +++++++++++ .../Call/MXCallAnswerEventContent.h | 36 ++++ .../Call/MXCallAnswerEventContent.m | 35 ++++ MatrixSDK/JSONModels/Call/MXCallCandidate.h | 40 ++++ MatrixSDK/JSONModels/Call/MXCallCandidate.m | 50 +++++ .../Call/MXCallCandidatesEventContent.h | 36 ++++ .../Call/MXCallCandidatesEventContent.m | 35 ++++ .../JSONModels/Call/MXCallEventContent.h | 41 ++++ .../JSONModels/Call/MXCallEventContent.m | 57 ++++++ .../Call/MXCallHangupEventContent.h | 30 +++ .../Call/MXCallHangupEventContent.m | 34 ++++ .../Call/MXCallInviteEventContent.h | 48 +++++ .../Call/MXCallInviteEventContent.m | 41 ++++ .../Call/MXCallSessionDescription.h | 35 ++++ .../Call/MXCallSessionDescription.m | 33 ++++ .../JSONModels/Call/MXTurnServerResponse.h | 56 ++++++ .../JSONModels/Call/MXTurnServerResponse.m | 64 +++++++ MatrixSDK/JSONModels/MXJSONModels.h | 175 ------------------ MatrixSDK/JSONModels/MXJSONModels.m | 173 ----------------- MatrixSDK/MXRestClient.h | 1 + MatrixSDK/VoIP/MXCall.h | 5 - MatrixSDK/VoIP/MXCall.m | 5 +- MatrixSDK/VoIP/MXCallManager.m | 5 + MatrixSDKTests/Mocks/MXMockCallStack.h | 2 +- 24 files changed, 786 insertions(+), 355 deletions(-) create mode 100644 MatrixSDK/JSONModels/Call/MXCallAnswerEventContent.h create mode 100644 MatrixSDK/JSONModels/Call/MXCallAnswerEventContent.m create mode 100644 MatrixSDK/JSONModels/Call/MXCallCandidate.h create mode 100644 MatrixSDK/JSONModels/Call/MXCallCandidate.m create mode 100644 MatrixSDK/JSONModels/Call/MXCallCandidatesEventContent.h create mode 100644 MatrixSDK/JSONModels/Call/MXCallCandidatesEventContent.m create mode 100644 MatrixSDK/JSONModels/Call/MXCallEventContent.h create mode 100644 MatrixSDK/JSONModels/Call/MXCallEventContent.m create mode 100644 MatrixSDK/JSONModels/Call/MXCallHangupEventContent.h create mode 100644 MatrixSDK/JSONModels/Call/MXCallHangupEventContent.m create mode 100644 MatrixSDK/JSONModels/Call/MXCallInviteEventContent.h create mode 100644 MatrixSDK/JSONModels/Call/MXCallInviteEventContent.m create mode 100644 MatrixSDK/JSONModels/Call/MXCallSessionDescription.h create mode 100644 MatrixSDK/JSONModels/Call/MXCallSessionDescription.m create mode 100644 MatrixSDK/JSONModels/Call/MXTurnServerResponse.h create mode 100644 MatrixSDK/JSONModels/Call/MXTurnServerResponse.m diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 8f84405ff6..164a9eda5a 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -1141,6 +1141,38 @@ C6FE1EF01E65C4F7008587E4 /* MXAnalyticsDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = C6FE1EEF1E65C4F7008587E4 /* MXAnalyticsDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC619D9224DD834B00663A80 /* MXPushGatewayRestClient.m in Sources */ = {isa = PBXBuildFile; fileRef = EC619D9024DD834B00663A80 /* MXPushGatewayRestClient.m */; }; EC619D9324DD834B00663A80 /* MXPushGatewayRestClient.h in Headers */ = {isa = PBXBuildFile; fileRef = EC619D9124DD834B00663A80 /* MXPushGatewayRestClient.h */; }; + EC97653B255BFED4000C36EF /* MXCallEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976539255BFED4000C36EF /* MXCallEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC97653C255BFED4000C36EF /* MXCallEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976539255BFED4000C36EF /* MXCallEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC97653D255BFED4000C36EF /* MXCallEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC97653A255BFED4000C36EF /* MXCallEventContent.m */; }; + EC97653E255BFED4000C36EF /* MXCallEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC97653A255BFED4000C36EF /* MXCallEventContent.m */; }; + EC976549255BFF35000C36EF /* MXCallInviteEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976547255BFF35000C36EF /* MXCallInviteEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC97654A255BFF35000C36EF /* MXCallInviteEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976547255BFF35000C36EF /* MXCallInviteEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC97654B255BFF35000C36EF /* MXCallInviteEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC976548255BFF35000C36EF /* MXCallInviteEventContent.m */; }; + EC97654C255BFF35000C36EF /* MXCallInviteEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC976548255BFF35000C36EF /* MXCallInviteEventContent.m */; }; + EC976553255BFFCB000C36EF /* MXCallCandidatesEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976551255BFFCB000C36EF /* MXCallCandidatesEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC976554255BFFCB000C36EF /* MXCallCandidatesEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976551255BFFCB000C36EF /* MXCallCandidatesEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC976555255BFFCB000C36EF /* MXCallCandidatesEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC976552255BFFCB000C36EF /* MXCallCandidatesEventContent.m */; }; + EC976556255BFFCB000C36EF /* MXCallCandidatesEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC976552255BFFCB000C36EF /* MXCallCandidatesEventContent.m */; }; + EC97655D255C002A000C36EF /* MXCallAnswerEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC97655B255C002A000C36EF /* MXCallAnswerEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC97655E255C002A000C36EF /* MXCallAnswerEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC97655B255C002A000C36EF /* MXCallAnswerEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC97655F255C002A000C36EF /* MXCallAnswerEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC97655C255C002A000C36EF /* MXCallAnswerEventContent.m */; }; + EC976560255C002A000C36EF /* MXCallAnswerEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC97655C255C002A000C36EF /* MXCallAnswerEventContent.m */; }; + EC976567255C0062000C36EF /* MXCallHangupEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976565255C0062000C36EF /* MXCallHangupEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC976568255C0062000C36EF /* MXCallHangupEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976565255C0062000C36EF /* MXCallHangupEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC976569255C0062000C36EF /* MXCallHangupEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC976566255C0062000C36EF /* MXCallHangupEventContent.m */; }; + EC97656A255C0062000C36EF /* MXCallHangupEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC976566255C0062000C36EF /* MXCallHangupEventContent.m */; }; + EC976571255C00D5000C36EF /* MXCallSessionDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = EC97656F255C00D5000C36EF /* MXCallSessionDescription.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC976572255C00D5000C36EF /* MXCallSessionDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = EC97656F255C00D5000C36EF /* MXCallSessionDescription.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC976573255C00D5000C36EF /* MXCallSessionDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = EC976570255C00D5000C36EF /* MXCallSessionDescription.m */; }; + EC976574255C00D5000C36EF /* MXCallSessionDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = EC976570255C00D5000C36EF /* MXCallSessionDescription.m */; }; + EC97657B255C0101000C36EF /* MXCallCandidate.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976579255C0101000C36EF /* MXCallCandidate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC97657C255C0101000C36EF /* MXCallCandidate.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976579255C0101000C36EF /* MXCallCandidate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC97657D255C0101000C36EF /* MXCallCandidate.m in Sources */ = {isa = PBXBuildFile; fileRef = EC97657A255C0101000C36EF /* MXCallCandidate.m */; }; + EC97657E255C0101000C36EF /* MXCallCandidate.m in Sources */ = {isa = PBXBuildFile; fileRef = EC97657A255C0101000C36EF /* MXCallCandidate.m */; }; + EC976585255C0127000C36EF /* MXTurnServerResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976583255C0127000C36EF /* MXTurnServerResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC976586255C0127000C36EF /* MXTurnServerResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976583255C0127000C36EF /* MXTurnServerResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC976587255C0127000C36EF /* MXTurnServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = EC976584255C0127000C36EF /* MXTurnServerResponse.m */; }; + EC976588255C0127000C36EF /* MXTurnServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = EC976584255C0127000C36EF /* MXTurnServerResponse.m */; }; ECAE7AEC24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = ECAE7AEB24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m */; }; F0173EAC1FCF0E8900B5F6A3 /* MXGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = F0173EAA1FCF0E8800B5F6A3 /* MXGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; F0173EAD1FCF0E8900B5F6A3 /* MXGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = F0173EAB1FCF0E8900B5F6A3 /* MXGroup.m */; }; @@ -1792,6 +1824,22 @@ E9B11A2D50665ADBB4B2F5C5 /* Pods-MatrixSDK-MatrixSDK-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixSDK-MatrixSDK-iOS.release.xcconfig"; path = "Target Support Files/Pods-MatrixSDK-MatrixSDK-iOS/Pods-MatrixSDK-MatrixSDK-iOS.release.xcconfig"; sourceTree = ""; }; EC619D9024DD834B00663A80 /* MXPushGatewayRestClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXPushGatewayRestClient.m; sourceTree = ""; }; EC619D9124DD834B00663A80 /* MXPushGatewayRestClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXPushGatewayRestClient.h; sourceTree = ""; }; + EC976539255BFED4000C36EF /* MXCallEventContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCallEventContent.h; sourceTree = ""; }; + EC97653A255BFED4000C36EF /* MXCallEventContent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCallEventContent.m; sourceTree = ""; }; + EC976547255BFF35000C36EF /* MXCallInviteEventContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCallInviteEventContent.h; sourceTree = ""; }; + EC976548255BFF35000C36EF /* MXCallInviteEventContent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCallInviteEventContent.m; sourceTree = ""; }; + EC976551255BFFCB000C36EF /* MXCallCandidatesEventContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCallCandidatesEventContent.h; sourceTree = ""; }; + EC976552255BFFCB000C36EF /* MXCallCandidatesEventContent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCallCandidatesEventContent.m; sourceTree = ""; }; + EC97655B255C002A000C36EF /* MXCallAnswerEventContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCallAnswerEventContent.h; sourceTree = ""; }; + EC97655C255C002A000C36EF /* MXCallAnswerEventContent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCallAnswerEventContent.m; sourceTree = ""; }; + EC976565255C0062000C36EF /* MXCallHangupEventContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCallHangupEventContent.h; sourceTree = ""; }; + EC976566255C0062000C36EF /* MXCallHangupEventContent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCallHangupEventContent.m; sourceTree = ""; }; + EC97656F255C00D5000C36EF /* MXCallSessionDescription.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCallSessionDescription.h; sourceTree = ""; }; + EC976570255C00D5000C36EF /* MXCallSessionDescription.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCallSessionDescription.m; sourceTree = ""; }; + EC976579255C0101000C36EF /* MXCallCandidate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCallCandidate.h; sourceTree = ""; }; + EC97657A255C0101000C36EF /* MXCallCandidate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCallCandidate.m; sourceTree = ""; }; + EC976583255C0127000C36EF /* MXTurnServerResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXTurnServerResponse.h; sourceTree = ""; }; + EC976584255C0127000C36EF /* MXTurnServerResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXTurnServerResponse.m; sourceTree = ""; }; ECAE7AEB24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTTPAdditionalHeadersTests.m; sourceTree = ""; }; ED2F344856EFFCA383E37B22 /* Pods-SDK-MatrixSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDK-MatrixSDK.release.xcconfig"; path = "Target Support Files/Pods-SDK-MatrixSDK/Pods-SDK-MatrixSDK.release.xcconfig"; sourceTree = ""; }; EDC74874AB2D86EFEE912B04 /* Pods-MatrixSDK-MatrixSDK-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixSDK-MatrixSDK-macOS.debug.xcconfig"; path = "Target Support Files/Pods-MatrixSDK-MatrixSDK-macOS/Pods-MatrixSDK-MatrixSDK-macOS.debug.xcconfig"; sourceTree = ""; }; @@ -2475,6 +2523,7 @@ 3281E8B219E42DFE00976E1A /* JSONModels */ = { isa = PBXGroup; children = ( + EC976538255BFE46000C36EF /* Call */, 327E9AE422859FE300A98BC1 /* Aggregations */, 323547D12226D3F500F15F94 /* AutoDiscovery */, 3275FD9121A6B46600B9C13D /* Authentication */, @@ -3166,6 +3215,29 @@ path = PushGateway; sourceTree = ""; }; + EC976538255BFE46000C36EF /* Call */ = { + isa = PBXGroup; + children = ( + EC976539255BFED4000C36EF /* MXCallEventContent.h */, + EC97653A255BFED4000C36EF /* MXCallEventContent.m */, + EC976547255BFF35000C36EF /* MXCallInviteEventContent.h */, + EC976548255BFF35000C36EF /* MXCallInviteEventContent.m */, + EC976551255BFFCB000C36EF /* MXCallCandidatesEventContent.h */, + EC976552255BFFCB000C36EF /* MXCallCandidatesEventContent.m */, + EC97655B255C002A000C36EF /* MXCallAnswerEventContent.h */, + EC97655C255C002A000C36EF /* MXCallAnswerEventContent.m */, + EC976565255C0062000C36EF /* MXCallHangupEventContent.h */, + EC976566255C0062000C36EF /* MXCallHangupEventContent.m */, + EC97656F255C00D5000C36EF /* MXCallSessionDescription.h */, + EC976570255C00D5000C36EF /* MXCallSessionDescription.m */, + EC976579255C0101000C36EF /* MXCallCandidate.h */, + EC97657A255C0101000C36EF /* MXCallCandidate.m */, + EC976583255C0127000C36EF /* MXTurnServerResponse.h */, + EC976584255C0127000C36EF /* MXTurnServerResponse.m */, + ); + path = Call; + sourceTree = ""; + }; F03EF4F91DF014D9009DF592 /* Media */ = { isa = PBXGroup; children = ( @@ -3211,6 +3283,7 @@ 32A1515B1DB525DA00400192 /* NSObject+sortedKeys.h in Headers */, 329D3E621E251027002E2F1E /* MXRoomSummaryUpdater.h in Headers */, 321B413F1E09937E009EEEC7 /* MXRoomSummary.h in Headers */, + EC97653B255BFED4000C36EF /* MXCallEventContent.h in Headers */, 32AF9284240EA2430008A0FD /* MXSecretShareRequest.h in Headers */, B17982F52119E4A2001FD722 /* MXRoomCreateContent.h in Headers */, 32792BDC2296B90A00F4FC9D /* MXAggregatedEditsUpdater.h in Headers */, @@ -3253,6 +3326,7 @@ 92634B821EF2E3C400DB9F60 /* MXCallKitConfiguration.h in Headers */, 32618E7120ED2DF500E1D2EA /* MXFilterJSONModel.h in Headers */, B146D47221A5939100D8C2C6 /* MXRealmHelper.h in Headers */, + EC976567255C0062000C36EF /* MXCallHangupEventContent.h in Headers */, 322360521A8E610500A3CA81 /* MXPushRuleDisplayNameCondtionChecker.h in Headers */, 32B94E01228EDEBC00716A26 /* MXReactionRelation.h in Headers */, F03EF5001DF014D9009DF592 /* MXMediaManager.h in Headers */, @@ -3333,6 +3407,7 @@ 32549AF923F2E2790002576B /* MXKeyVerificationReady.h in Headers */, B17285792100C8EA0052C51E /* MXSendReplyEventStringsLocalizable.h in Headers */, 32A151521DAF8A7200400192 /* MXQueuedEncryption.h in Headers */, + EC976571255C00D5000C36EF /* MXCallSessionDescription.h in Headers */, 32A30B181FB4813400C8309E /* MXIncomingRoomKeyRequestManager.h in Headers */, F03EF5081DF071D5009DF592 /* MXEncryptedAttachments.h in Headers */, 3291DC8323DF52E10009732F /* MXRoomCreationParameters.h in Headers */, @@ -3363,7 +3438,9 @@ 32618E7B20EFA45B00E1D2EA /* MXRoomMembers.h in Headers */, 3240969D1F9F751600DBA607 /* MXPushRuleSenderNotificationPermissionConditionChecker.h in Headers */, 329E808C224E2E1B00A48C3A /* MXOutgoingSASTransaction.h in Headers */, + EC976549255BFF35000C36EF /* MXCallInviteEventContent.h in Headers */, B146D4D521A5A44E00D8C2C6 /* MXScanRealmProvider.h in Headers */, + EC97657B255C0101000C36EF /* MXCallCandidate.h in Headers */, 32CEEF4323AD2A6C0039BA98 /* MXCrossSigningKey.h in Headers */, 327E9ABC2284521C00A98BC1 /* MXEventUnsignedData.h in Headers */, B10AFB4722AA8A8E0092E6AF /* MXEventEditsListener.h in Headers */, @@ -3371,6 +3448,7 @@ 32F634AB1FC5E3480054EF49 /* MXEventDecryptionResult.h in Headers */, 322A51C71D9BBD3C00C8536D /* MXOlmDevice.h in Headers */, 32999DE322DCD1AD004FF987 /* MXPusherData.h in Headers */, + EC976553255BFFCB000C36EF /* MXCallCandidatesEventContent.h in Headers */, 32442FB121EDD21300D2411B /* MXKeyBackupPassword.h in Headers */, 320DFDE419DD99B60068622A /* MXRestClient.h in Headers */, 3252DCAE224BE5D40032264F /* MXKeyVerificationManager.h in Headers */, @@ -3388,6 +3466,7 @@ 32A9E8241EF4026E0081358A /* MXBackgroundModeHandler.h in Headers */, B17982F72119E4A2001FD722 /* MXRoomPredecessorInfo.h in Headers */, 32BED28F1B00A23F00E668FE /* MXCallStack.h in Headers */, + EC976585255C0127000C36EF /* MXTurnServerResponse.h in Headers */, F03EF4FE1DF014D9009DF592 /* MXMediaLoader.h in Headers */, 3264DB911CEC528D00B99881 /* MXAccountData.h in Headers */, 32FE41361D0AB7070060835E /* MXEnumConstants.h in Headers */, @@ -3444,6 +3523,7 @@ 324DD2A0246AE1EF00377005 /* MXEncryptedSecretContent.h in Headers */, B11BD45922CB58850064D8B0 /* MXReplyEventFormattedBodyParts.h in Headers */, 32BBAE702178E99100D85F46 /* MXKeyBackupData.h in Headers */, + EC97655D255C002A000C36EF /* MXCallAnswerEventContent.h in Headers */, 32999DDF22DCD183004FF987 /* MXPusher.h in Headers */, 327E37B61A974F75007F026F /* MXLogger.h in Headers */, 320A883C217F4E35002EA952 /* MXMegolmBackupCreationInfo.h in Headers */, @@ -3494,6 +3574,7 @@ B14EF2AC2397E90400758AF0 /* MXMemoryStore.h in Headers */, B14EF2AD2397E90400758AF0 /* MXEventAnnotationChunk.h in Headers */, B14EF2AE2397E90400758AF0 /* (null) in Headers */, + EC976568255C0062000C36EF /* MXCallHangupEventContent.h in Headers */, B14EF2AF2397E90400758AF0 /* MXWellKnown.h in Headers */, B14EF2B02397E90400758AF0 /* MXRecoveryKey.h in Headers */, B14EF2B12397E90400758AF0 /* MXRealmAggregationsStore.h in Headers */, @@ -3515,6 +3596,7 @@ B14EF2BD2397E90400758AF0 /* MXAggregationPaginatedResponse.h in Headers */, B14EF2BE2397E90400758AF0 /* MXEvent.h in Headers */, B14EF2BF2397E90400758AF0 /* MXRoomThirdPartyInvite.h in Headers */, + EC97657C255C0101000C36EF /* MXCallCandidate.h in Headers */, B14EF2C02397E90400758AF0 /* MXRoomNameStringsLocalizable.h in Headers */, B14EF2C12397E90400758AF0 /* MXKeyBackup.h in Headers */, B14EF2C22397E90400758AF0 /* MXCallKitConfiguration.h in Headers */, @@ -3558,6 +3640,7 @@ 3274538523FD69D600438328 /* MXKeyVerificationRequestByToDeviceJSONModel.h in Headers */, B14EF2DD2397E90400758AF0 /* MXEventsEnumerator.h in Headers */, B14EF2DE2397E90400758AF0 /* MXDeviceList.h in Headers */, + EC976586255C0127000C36EF /* MXTurnServerResponse.h in Headers */, B14EF2DF2397E90400758AF0 /* MXDecryptionResult.h in Headers */, B14EF2E02397E90400758AF0 /* MXCall.h in Headers */, B14EF2E12397E90400758AF0 /* MXContentScanEncryptedBody.h in Headers */, @@ -3567,6 +3650,7 @@ B14EF2E42397E90400758AF0 /* MXStore.h in Headers */, B14EF2E52397E90400758AF0 /* MXRoomNameDefaultStringLocalizations.h in Headers */, 32AF928B240EA3880008A0FD /* MXSecretShareSend.h in Headers */, + EC97653C255BFED4000C36EF /* MXCallEventContent.h in Headers */, B14EF2E62397E90400758AF0 /* MXMegolmEncryption.h in Headers */, B14EF2E72397E90400758AF0 /* MXLRUCache.h in Headers */, 32AF929824115D8B0008A0FD /* MXPendingSecretShareRequest.h in Headers */, @@ -3604,6 +3688,7 @@ B14EF3032397E90400758AF0 /* MXEventsByTypesEnumeratorOnArray.h in Headers */, B14EF3042397E90400758AF0 /* MXKeyVerificationTransaction.h in Headers */, B14EF3052397E90400758AF0 /* MXIdentityServerHashDetails.h in Headers */, + EC976554255BFFCB000C36EF /* MXCallCandidatesEventContent.h in Headers */, B14EF3062397E90400758AF0 /* MXEncrypting.h in Headers */, 324AAC7D2399143400380A66 /* MXKeyVerificationAccept.h in Headers */, B14EF3072397E90400758AF0 /* MXScanManager.h in Headers */, @@ -3613,6 +3698,7 @@ B14EF30B2397E90400758AF0 /* MXWellKnownBaseConfig.h in Headers */, B14EF30C2397E90400758AF0 /* MXDeviceInfo.h in Headers */, 32B0E34023A378320054FF1A /* MXEventReference.h in Headers */, + EC97655E255C002A000C36EF /* MXCallAnswerEventContent.h in Headers */, 324AAC802399143400380A66 /* MXKeyVerificationJSONModel.h in Headers */, B14EF30D2397E90400758AF0 /* MXEventRelations.h in Headers */, B14EF30E2397E90400758AF0 /* MXPushRuleRoomMemberCountConditionChecker.h in Headers */, @@ -3658,6 +3744,7 @@ B14EF32C2397E90400758AF0 /* MXRoomPredecessorInfo.h in Headers */, B14EF32D2397E90400758AF0 /* MXCallStack.h in Headers */, B14EF32E2397E90400758AF0 /* MXMediaLoader.h in Headers */, + EC976572255C00D5000C36EF /* MXCallSessionDescription.h in Headers */, B14EF32F2397E90400758AF0 /* MXAccountData.h in Headers */, B14EF3302397E90400758AF0 /* MXEnumConstants.h in Headers */, 3291DC8423DF52E20009732F /* MXRoomCreationParameters.h in Headers */, @@ -3680,6 +3767,7 @@ B14EF33E2397E90400758AF0 /* MXOlmInboundGroupSession.h in Headers */, B14EF33F2397E90400758AF0 /* (null) in Headers */, B14EF3402397E90400758AF0 /* MXKeyBackupVersionTrust.h in Headers */, + EC97654A255BFF35000C36EF /* MXCallInviteEventContent.h in Headers */, 320B3935239FA56900BE2C06 /* MXKeyVerificationByDMRequest.h in Headers */, B14EF3412397E90400758AF0 /* MXOutgoingRoomKeyRequestManager.h in Headers */, B14EF3422397E90400758AF0 /* MXPeekingRoomSummary.h in Headers */, @@ -4031,6 +4119,7 @@ 324DD2A2246AE1EF00377005 /* MXEncryptedSecretContent.m in Sources */, 329FB1761A0A3A1600A5E88E /* MXRoomMember.m in Sources */, B1136965230AC9D900E2B2FA /* MXIdentityService.m in Sources */, + EC97654B255BFF35000C36EF /* MXCallInviteEventContent.m in Sources */, B11BD44922CB56790064D8B0 /* MXReplyEventParser.m in Sources */, 323360701A403A0D0071A488 /* MXFileStore.m in Sources */, B1136967230C1E8600E2B2FA /* MXIdentityService.swift in Sources */, @@ -4059,6 +4148,7 @@ 32F945F71FAB83D900622468 /* MXIncomingRoomKeyRequest.m in Sources */, 32D2CC09234336D6002BD8CA /* MX3PidAddManager.swift in Sources */, F0173EAD1FCF0E8900B5F6A3 /* MXGroup.m in Sources */, + EC97657D255C0101000C36EF /* MXCallCandidate.m in Sources */, 32CEEF5123B0AB030039BA98 /* MXCrossSigning.m in Sources */, 32720D9F222EAA6F0086FFF5 /* MXAutoDiscovery.m in Sources */, 3297912923A93D4B00F7BB9B /* MXKeyVerification.m in Sources */, @@ -4088,6 +4178,7 @@ B10AFB4822AA8A8E0092E6AF /* MXEventEditsListener.m in Sources */, C6D5D60E1E4FBD2900706C0F /* MXSession.swift in Sources */, 320DFDE319DD99B60068622A /* MXError.m in Sources */, + EC976587255C0127000C36EF /* MXTurnServerResponse.m in Sources */, 327C3E4D23A39D91006183D1 /* MXAggregatedReferencesUpdater.m in Sources */, 325380E9228DAD4A00ADDEFA /* MXAggregatedReactions.m in Sources */, 021AFBA52179E91900742B2C /* MXEncryptedContentKey.m in Sources */, @@ -4140,9 +4231,11 @@ 32A1515C1DB525DA00400192 /* NSObject+sortedKeys.m in Sources */, 32792BD52295A86600F4FC9D /* MXAggregatedReactionsUpdater.m in Sources */, 32618E7C20EFA45B00E1D2EA /* MXRoomMembers.m in Sources */, + EC976555255BFFCB000C36EF /* MXCallCandidatesEventContent.m in Sources */, 32B0E34123A378320054FF1A /* MXEventReference.m in Sources */, 324DD2A8246AE81300377005 /* MXSecretStorageKeyContent.m in Sources */, 02CAD43A217DD12F0074700B /* MXContentScanEncryptedBody.m in Sources */, + EC976573255C00D5000C36EF /* MXCallSessionDescription.m in Sources */, 325AF3E324897D9400EF937D /* MXSecretRecoveryResult.m in Sources */, F03EF5091DF071D5009DF592 /* MXEncryptedAttachments.m in Sources */, 324DD2C1246D658500377005 /* MXHkdfSha256.m in Sources */, @@ -4176,6 +4269,7 @@ B19A30A82404257700FB6F35 /* MXSASKeyVerificationStart.m in Sources */, 02CAD439217DD12F0074700B /* MXContentScanResult.m in Sources */, 32133016228AF4EF0070BA9B /* MXRealmAggregationsStore.m in Sources */, + EC976569255C0062000C36EF /* MXCallHangupEventContent.m in Sources */, B19A30A62404257700FB6F35 /* MXQRCodeKeyVerificationStart.m in Sources */, B146D47B21A5958400D8C2C6 /* MXAntivirusScanStatusFormatter.m in Sources */, 32133022228BF7BC0070BA9B /* MXReactionCountChange.m in Sources */, @@ -4185,6 +4279,7 @@ F0C34CBB1C18C93700C36F09 /* MXSDKOptions.m in Sources */, 320BBF441D6C81550079890E /* MXEventsEnumeratorOnArray.m in Sources */, 32618E7220ED2DF500E1D2EA /* MXFilterJSONModel.m in Sources */, + EC97655F255C002A000C36EF /* MXCallAnswerEventContent.m in Sources */, 323F8865212D4E480001C73C /* MXMatrixVersions.m in Sources */, 32133026228BFA800070BA9B /* MXReactionCountChangeListener.m in Sources */, 320A883D217F4E35002EA952 /* MXMegolmBackupCreationInfo.m in Sources */, @@ -4217,6 +4312,7 @@ 32DC15D11A8CF7AE006F9AD3 /* MXNotificationCenter.m in Sources */, 32637ED51E5B00400011E20D /* MXDeviceList.m in Sources */, 327A5F55239805F600ED6329 /* MXKeyVerificationMac.m in Sources */, + EC97653D255BFED4000C36EF /* MXCallEventContent.m in Sources */, B17982FA2119E4A2001FD722 /* MXRoomCreateContent.m in Sources */, 32A9E8261EF4026E0081358A /* MXUIKitBackgroundModeHandler.m in Sources */, B19A30D024042F0800FB6F35 /* MXSelfVerifyingMasterKeyTrustedQRCodeData.m in Sources */, @@ -4323,6 +4419,7 @@ B14EF1D52397E90400758AF0 /* MXEventAnnotation.m in Sources */, B14EF1D62397E90400758AF0 /* MXPusher.m in Sources */, B14EF1D72397E90400758AF0 /* MXMediaLoader.m in Sources */, + EC976560255C002A000C36EF /* MXCallAnswerEventContent.m in Sources */, 32549AF823F2E2790002576B /* MXKeyVerificationReady.m in Sources */, B14EF1D82397E90400758AF0 /* MXMegolmBackupAuthData.m in Sources */, B14EF1D92397E90400758AF0 /* MXReactionCount.m in Sources */, @@ -4330,6 +4427,7 @@ B14EF1DB2397E90400758AF0 /* MXPushRuleEventMatchConditionChecker.m in Sources */, B14EF1DC2397E90400758AF0 /* MXRealmReactionRelation.m in Sources */, B14EF1DD2397E90400758AF0 /* MXEventsByTypesEnumeratorOnArray.m in Sources */, + EC97654C255BFF35000C36EF /* MXCallInviteEventContent.m in Sources */, B14EF1DE2397E90400758AF0 /* MXRealmAggregationsMapper.m in Sources */, 3274538723FD69D600438328 /* MXKeyVerificationRequestByToDeviceJSONModel.m in Sources */, B14EF1DF2397E90400758AF0 /* MXAggregatedEditsUpdater.m in Sources */, @@ -4385,6 +4483,7 @@ B14EF2062397E90400758AF0 /* MXGroup.m in Sources */, B14EF2072397E90400758AF0 /* MXAutoDiscovery.m in Sources */, 3297913123AA126500F7BB9B /* MXKeyVerificationStatusResolver.m in Sources */, + EC976574255C00D5000C36EF /* MXCallSessionDescription.m in Sources */, B14EF2082397E90400758AF0 /* MX3PidAddManager.m in Sources */, B14EF2092397E90400758AF0 /* MXCallKitConfiguration.m in Sources */, 324DD2A9246AE81300377005 /* MXSecretStorageKeyContent.m in Sources */, @@ -4469,9 +4568,11 @@ B14EF24D2397E90400758AF0 /* MXOutgoingRoomKeyRequest.m in Sources */, B14EF24E2397E90400758AF0 /* MXAllowedCertificates.m in Sources */, B14EF24F2397E90400758AF0 /* MXLRUCache.m in Sources */, + EC97657E255C0101000C36EF /* MXCallCandidate.m in Sources */, B14EF2502397E90400758AF0 /* MXIncomingRoomKeyRequestManager.m in Sources */, B14EF2512397E90400758AF0 /* MXRoomEventFilter.m in Sources */, B14EF2522397E90400758AF0 /* MXLoginPolicyData.m in Sources */, + EC976556255BFFCB000C36EF /* MXCallCandidatesEventContent.m in Sources */, B19A30C72404268600FB6F35 /* MXQRCodeDataBuilder.m in Sources */, B14EF2532397E90400758AF0 /* MXPushRuleSenderNotificationPermissionConditionChecker.m in Sources */, 324AAC752399140D00380A66 /* MXKeyVerificationAccept.m in Sources */, @@ -4527,8 +4628,11 @@ B14EF27A2397E90400758AF0 /* MXSessionEventListener.swift in Sources */, B14EF27B2397E90400758AF0 /* MXOlmSessionResult.m in Sources */, B14EF27C2397E90400758AF0 /* MXRestClient.swift in Sources */, + EC97656A255C0062000C36EF /* MXCallHangupEventContent.m in Sources */, + EC976588255C0127000C36EF /* MXTurnServerResponse.m in Sources */, 32AF929A24115D8B0008A0FD /* MXPendingSecretShareRequest.m in Sources */, B14EF27D2397E90400758AF0 /* MXKeyBackup.m in Sources */, + EC97653E255BFED4000C36EF /* MXCallEventContent.m in Sources */, B14EF27E2397E90400758AF0 /* MXCrypto.m in Sources */, B14EF27F2397E90400758AF0 /* MXRoomPredecessorInfo.m in Sources */, B14EF2802397E90400758AF0 /* MXServiceTerms.m in Sources */, diff --git a/MatrixSDK/JSONModels/Call/MXCallAnswerEventContent.h b/MatrixSDK/JSONModels/Call/MXCallAnswerEventContent.h new file mode 100644 index 0000000000..f70066ea49 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXCallAnswerEventContent.h @@ -0,0 +1,36 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "MXCallEventContent.h" +#import "MXCallSessionDescription.h" + +/** + `MXCallAnswerEventContent` represents the content of a m.call.answer event. + */ +@interface MXCallAnswerEventContent : MXCallEventContent + +/** + A unique identifier for the call. + */ +@property (nonatomic) NSString *callId; + +/** + The session description. + */ +@property (nonatomic) MXCallSessionDescription *answer; + +@end diff --git a/MatrixSDK/JSONModels/Call/MXCallAnswerEventContent.m b/MatrixSDK/JSONModels/Call/MXCallAnswerEventContent.m new file mode 100644 index 0000000000..9e5d4137e1 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXCallAnswerEventContent.m @@ -0,0 +1,35 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXCallAnswerEventContent.h" + +@implementation MXCallAnswerEventContent + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXCallAnswerEventContent *callAnswerEventContent = [[MXCallAnswerEventContent alloc] init]; + if (callAnswerEventContent) + { + MXJSONModelSetString(callAnswerEventContent.callId, JSONDictionary[@"call_id"]); + MXJSONModelSetNumber(callAnswerEventContent.versionNumber, JSONDictionary[@"version"]); + MXJSONModelSetString(callAnswerEventContent.versionString, JSONDictionary[@"version"]); + MXJSONModelSetMXJSONModel(callAnswerEventContent.answer, MXCallSessionDescription, JSONDictionary[@"answer"]); + } + + return callAnswerEventContent; +} + +@end diff --git a/MatrixSDK/JSONModels/Call/MXCallCandidate.h b/MatrixSDK/JSONModels/Call/MXCallCandidate.h new file mode 100644 index 0000000000..b280270864 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXCallCandidate.h @@ -0,0 +1,40 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "MXJSONModel.h" + +/** + `MXCallCandidate` represents a candidate description. + */ +@interface MXCallCandidate : MXJSONModel + +/** + The SDP media type this candidate is intended for. + */ +@property (nonatomic) NSString *sdpMid; + +/** + The index of the SDP 'm' line this candidate is intended for. + */ +@property (nonatomic) NSUInteger sdpMLineIndex; + +/** + The SDP 'a' line of the candidate. + */ +@property (nonatomic) NSString *candidate; + +@end diff --git a/MatrixSDK/JSONModels/Call/MXCallCandidate.m b/MatrixSDK/JSONModels/Call/MXCallCandidate.m new file mode 100644 index 0000000000..7844f23558 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXCallCandidate.m @@ -0,0 +1,50 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXCallCandidate.h" + +@implementation MXCallCandidate + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXCallCandidate *callCandidate = [[MXCallCandidate alloc] init]; + if (callCandidate) + { + MXJSONModelSetString(callCandidate.sdpMid, JSONDictionary[@"sdpMid"]); + MXJSONModelSetUInteger(callCandidate.sdpMLineIndex, JSONDictionary[@"sdpMLineIndex"]); + MXJSONModelSetString(callCandidate.candidate, JSONDictionary[@"candidate"]); + } + + return callCandidate; +} + +- (NSDictionary *)JSONDictionary +{ + NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionary]; + + JSONDictionary[@"sdpMid"] = _sdpMid; + JSONDictionary[@"sdpMLineIndex"] = @(_sdpMLineIndex); + JSONDictionary[@"candidate"] = _candidate; + + return JSONDictionary; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@" %@ - %tu - %@", self, _sdpMid, _sdpMLineIndex, _candidate]; +} + +@end diff --git a/MatrixSDK/JSONModels/Call/MXCallCandidatesEventContent.h b/MatrixSDK/JSONModels/Call/MXCallCandidatesEventContent.h new file mode 100644 index 0000000000..26057c593c --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXCallCandidatesEventContent.h @@ -0,0 +1,36 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "MXCallEventContent.h" +#import "MXCallCandidate.h" + +/** + `MXCallCandidatesEventContent` represents the content of a m.call.candidates event. + */ +@interface MXCallCandidatesEventContent : MXCallEventContent + +/** + The ID of the call this event relates to. + */ +@property (nonatomic) NSString *callId; + +/** + Array of object describing the candidates (@see MXCallCandidate). + */ +@property (nonatomic) NSArray *candidates; + +@end diff --git a/MatrixSDK/JSONModels/Call/MXCallCandidatesEventContent.m b/MatrixSDK/JSONModels/Call/MXCallCandidatesEventContent.m new file mode 100644 index 0000000000..b6a4b603da --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXCallCandidatesEventContent.m @@ -0,0 +1,35 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXCallCandidatesEventContent.h" + +@implementation MXCallCandidatesEventContent + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXCallCandidatesEventContent *callCandidatesEventContent = [[MXCallCandidatesEventContent alloc] init]; + if (callCandidatesEventContent) + { + MXJSONModelSetString(callCandidatesEventContent.callId, JSONDictionary[@"call_id"]); + MXJSONModelSetNumber(callCandidatesEventContent.versionNumber, JSONDictionary[@"version"]); + MXJSONModelSetString(callCandidatesEventContent.versionString, JSONDictionary[@"version"]); + MXJSONModelSetMXJSONModelArray(callCandidatesEventContent.candidates, MXCallCandidate, JSONDictionary[@"candidates"]); + } + + return callCandidatesEventContent; +} + +@end diff --git a/MatrixSDK/JSONModels/Call/MXCallEventContent.h b/MatrixSDK/JSONModels/Call/MXCallEventContent.h new file mode 100644 index 0000000000..404c259a86 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXCallEventContent.h @@ -0,0 +1,41 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "MXJSONModel.h" + +/** + Call version + */ +extern NSString *const kMXCallVersion; + +/// Base class for event contents of call events. +@interface MXCallEventContent : MXJSONModel + +/** + The version of the VoIP specification this message adheres to. Can be nil. @see `version`. + */ +@property (nonatomic, copy) NSNumber *versionNumber; + +/** + The version of the VoIP specification this message adheres to. Can be nil. @see `version`. + */ +@property (nonatomic, copy) NSString *versionString; + +/// Derived value from versionNumber or versionString +- (NSString *)version; + +@end diff --git a/MatrixSDK/JSONModels/Call/MXCallEventContent.m b/MatrixSDK/JSONModels/Call/MXCallEventContent.m new file mode 100644 index 0000000000..912603e0b6 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXCallEventContent.m @@ -0,0 +1,57 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXCallEventContent.h" + +NSString *const kMXCallVersion = @"1"; + +static NSArray *kAcceptedCallVersions = nil; + +@implementation MXCallEventContent + ++ (void)initialize +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + kAcceptedCallVersions = @[ + @"0", + kMXCallVersion + ]; + }); +} + +- (NSString *)version +{ + NSString *_version; + + if (self.versionString) + { + _version = self.versionString; + } + else if (self.versionNumber) + { + _version = self.versionNumber.description; + } + + if (_version && [kAcceptedCallVersions containsObject:_version]) + { + return _version; + } + + return kMXCallVersion; +} + +@end diff --git a/MatrixSDK/JSONModels/Call/MXCallHangupEventContent.h b/MatrixSDK/JSONModels/Call/MXCallHangupEventContent.h new file mode 100644 index 0000000000..5af70b4501 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXCallHangupEventContent.h @@ -0,0 +1,30 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "MXCallEventContent.h" + +/** + `MXCallHangupEventContent` represents the content of a m.call.hangup event. + */ +@interface MXCallHangupEventContent : MXCallEventContent + +/** + A unique identifier for the call. + */ +@property (nonatomic) NSString *callId; + +@end diff --git a/MatrixSDK/JSONModels/Call/MXCallHangupEventContent.m b/MatrixSDK/JSONModels/Call/MXCallHangupEventContent.m new file mode 100644 index 0000000000..2779294e6e --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXCallHangupEventContent.m @@ -0,0 +1,34 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXCallHangupEventContent.h" + +@implementation MXCallHangupEventContent + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXCallHangupEventContent *callHangupEventContent = [[MXCallHangupEventContent alloc] init]; + if (callHangupEventContent) + { + MXJSONModelSetString(callHangupEventContent.callId, JSONDictionary[@"call_id"]); + MXJSONModelSetNumber(callHangupEventContent.versionNumber, JSONDictionary[@"version"]); + MXJSONModelSetString(callHangupEventContent.versionString, JSONDictionary[@"version"]); + } + + return callHangupEventContent; +} + +@end diff --git a/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.h b/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.h new file mode 100644 index 0000000000..5e42358c15 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.h @@ -0,0 +1,48 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "MXCallEventContent.h" +#import "MXCallSessionDescription.h" + +/** + `MXCallInviteEventContent` represents the content of a m.call.invite event. + */ +@interface MXCallInviteEventContent : MXCallEventContent + +/** + A unique identifier for the call. + */ +@property (nonatomic) NSString *callId; + +/** + The session description. + */ +@property (nonatomic) MXCallSessionDescription *offer; + +/** + The time in milliseconds that the invite is valid for. + Once the invite age exceeds this value, clients should discard it. + They should also no longer show the call as awaiting an answer in the UI. + */ +@property (nonatomic) NSUInteger lifetime; + +/** + Indicate whether the invitation is for a video call. + */ +- (BOOL)isVideoCall; + +@end diff --git a/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.m b/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.m new file mode 100644 index 0000000000..2f9c080738 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.m @@ -0,0 +1,41 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXCallInviteEventContent.h" + +@implementation MXCallInviteEventContent + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXCallInviteEventContent *callInviteEventContent = [[MXCallInviteEventContent alloc] init]; + if (callInviteEventContent) + { + MXJSONModelSetString(callInviteEventContent.callId, JSONDictionary[@"call_id"]); + MXJSONModelSetMXJSONModel(callInviteEventContent.offer, MXCallSessionDescription, JSONDictionary[@"offer"]); + MXJSONModelSetNumber(callInviteEventContent.versionNumber, JSONDictionary[@"version"]); + MXJSONModelSetString(callInviteEventContent.versionString, JSONDictionary[@"version"]); + MXJSONModelSetUInteger(callInviteEventContent.lifetime, JSONDictionary[@"lifetime"]); + } + + return callInviteEventContent; +} + +- (BOOL)isVideoCall +{ + return (NSNotFound != [self.offer.sdp rangeOfString:@"m=video"].location); +} + +@end diff --git a/MatrixSDK/JSONModels/Call/MXCallSessionDescription.h b/MatrixSDK/JSONModels/Call/MXCallSessionDescription.h new file mode 100644 index 0000000000..bd2b4d092d --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXCallSessionDescription.h @@ -0,0 +1,35 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "MXJSONModel.h" + +/** + `MXCallOffer` represents a call session description. + */ +@interface MXCallSessionDescription : MXJSONModel + +/** + The type of session description. Can be 'offer' or 'answer'. + */ +@property (nonatomic) NSString *type; + +/** + The SDP text of the session description. + */ +@property (nonatomic) NSString *sdp; + +@end diff --git a/MatrixSDK/JSONModels/Call/MXCallSessionDescription.m b/MatrixSDK/JSONModels/Call/MXCallSessionDescription.m new file mode 100644 index 0000000000..ad00558e94 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXCallSessionDescription.m @@ -0,0 +1,33 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXCallSessionDescription.h" + +@implementation MXCallSessionDescription + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXCallSessionDescription *callSessionDescription = [[MXCallSessionDescription alloc] init]; + if (callSessionDescription) + { + MXJSONModelSetString(callSessionDescription.type, JSONDictionary[@"type"]); + MXJSONModelSetString(callSessionDescription.sdp, JSONDictionary[@"sdp"]); + } + + return callSessionDescription; +} + +@end diff --git a/MatrixSDK/JSONModels/Call/MXTurnServerResponse.h b/MatrixSDK/JSONModels/Call/MXTurnServerResponse.h new file mode 100644 index 0000000000..84626bb5f5 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXTurnServerResponse.h @@ -0,0 +1,56 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "MXJSONModel.h" + +/** + `MXTurnServerResponse` represents the response to turnServer request. + It provides TURN server configuration advised by the homeserver. + */ +@interface MXTurnServerResponse : MXJSONModel + +/** + The username of the Matrix user on the TURN server. + */ +@property (nonatomic) NSString *username; + +/** + The associated password. + */ +@property (nonatomic) NSString *password; + +/** + The list URIs of TURN servers - including STUN servers. + The URI scheme obeys to http://tools.ietf.org/html/rfc7064#section-3.1 + and http://tools.ietf.org/html/rfc7065#section-3.1 + */ +@property (nonatomic) NSArray *uris; + +/** + Time To Live. The time is seconds this data is still valid. + It is computed by the user's homeserver when the request is made. + Then, the SDK updates the property each time it is read. + */ +@property (nonatomic) NSUInteger ttl; + +/** + The `ttl` value transcoded to an absolute date, a timestamp in milliseconds + based on the device clock. + */ +@property (nonatomic) uint64_t ttlExpirationLocalTs; + +@end diff --git a/MatrixSDK/JSONModels/Call/MXTurnServerResponse.m b/MatrixSDK/JSONModels/Call/MXTurnServerResponse.m new file mode 100644 index 0000000000..6478adde96 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/MXTurnServerResponse.m @@ -0,0 +1,64 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXTurnServerResponse.h" + +@implementation MXTurnServerResponse + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXTurnServerResponse *turnServerResponse = [[MXTurnServerResponse alloc] init]; + if (turnServerResponse) + { + MXJSONModelSetString(turnServerResponse.username, JSONDictionary[@"username"]); + MXJSONModelSetString(turnServerResponse.password, JSONDictionary[@"password"]); + MXJSONModelSetArray(turnServerResponse.uris, JSONDictionary[@"uris"]); + MXJSONModelSetUInteger(turnServerResponse.ttl, JSONDictionary[@"ttl"]); + } + + return turnServerResponse; +} + +- (instancetype)init +{ + self = [super init]; + if (self) + { + _ttlExpirationLocalTs = -1; + } + return self; +} + +- (void)setTtl:(NSUInteger)ttl +{ + if (-1 == _ttlExpirationLocalTs) + { + NSTimeInterval d = [[NSDate date] timeIntervalSince1970]; + _ttlExpirationLocalTs = (d + ttl) * 1000 ; + } +} + +- (NSUInteger)ttl +{ + NSUInteger ttl = 0; + if (-1 != _ttlExpirationLocalTs) + { + ttl = (NSUInteger)(_ttlExpirationLocalTs / 1000 - (uint64_t)[[NSDate date] timeIntervalSince1970]); + } + return ttl; +} + +@end diff --git a/MatrixSDK/JSONModels/MXJSONModels.h b/MatrixSDK/JSONModels/MXJSONModels.h index 5e67228d23..7897f7b379 100644 --- a/MatrixSDK/JSONModels/MXJSONModels.h +++ b/MatrixSDK/JSONModels/MXJSONModels.h @@ -1458,181 +1458,6 @@ FOUNDATION_EXPORT NSString *const kMXPushRuleScopeStringDevice; @end -#pragma mark - Voice over IP -#pragma mark - - -/** - `MXCallOffer` represents a call session description. - */ -@interface MXCallSessionDescription : MXJSONModel - - /** - The type of session description. Can be 'offer' or 'answer'. - */ - @property (nonatomic) NSString *type; - - /** - The SDP text of the session description. - */ - @property (nonatomic) NSString *sdp; - -@end - -/** - `MXCallInviteEventContent` represents the content of a m.call.invite event. - */ -@interface MXCallInviteEventContent : MXJSONModel - - /** - A unique identifier for the call. - */ - @property (nonatomic) NSString *callId; - - /** - The session description. - */ - @property (nonatomic) MXCallSessionDescription *offer; - - /** - The version of the VoIP specification this message adheres to. - */ - @property (nonatomic) NSUInteger version; - - /** - The time in milliseconds that the invite is valid for. - Once the invite age exceeds this value, clients should discard it. - They should also no longer show the call as awaiting an answer in the UI. - */ - @property (nonatomic) NSUInteger lifetime; - - /** - Indicate whether the invitation is for a video call. - */ - - (BOOL)isVideoCall; - -@end - -/** - `MXCallCandidate` represents a candidate description. - */ -@interface MXCallCandidate : MXJSONModel - - /** - The SDP media type this candidate is intended for. - */ - @property (nonatomic) NSString *sdpMid; - - /** - The index of the SDP 'm' line this candidate is intended for. - */ - @property (nonatomic) NSUInteger sdpMLineIndex; - - /** - The SDP 'a' line of the candidate. - */ - @property (nonatomic) NSString *candidate; - -@end - -/** - `MXCallCandidatesEventContent` represents the content of a m.call.candidates event. - */ -@interface MXCallCandidatesEventContent : MXJSONModel - - /** - The ID of the call this event relates to. - */ - @property (nonatomic) NSString *callId; - - /** - The version of the VoIP specification this message adheres to. - */ - @property (nonatomic) NSUInteger version; - - /** - Array of object describing the candidates (@see MXCallCandidate). - */ - @property (nonatomic) NSArray *candidates; - -@end - -/** - `MXCallAnswerEventContent` represents the content of a m.call.answer event. - */ -@interface MXCallAnswerEventContent : MXJSONModel - - /** - A unique identifier for the call. - */ - @property (nonatomic) NSString *callId; - - /** - The version of the VoIP specification this message adheres to. - */ - @property (nonatomic) NSUInteger version; - - /** - The session description. - */ - @property (nonatomic) MXCallSessionDescription *answer; - -@end - -/** - `MXCallHangupEventContent` represents the content of a m.call.hangup event. - */ -@interface MXCallHangupEventContent : MXJSONModel - - /** - A unique identifier for the call. - */ - @property (nonatomic) NSString *callId; - - /** - The version of the VoIP specification this message adheres to. - */ - @property (nonatomic) NSUInteger version; - -@end - -/** - `MXTurnServerResponse` represents the response to turnServer request. - It provides TURN server configuration advised by the homeserver. - */ -@interface MXTurnServerResponse : MXJSONModel - - /** - The username of the Matrix user on the TURN server. - */ - @property (nonatomic) NSString *username; - - /** - The associated password. - */ - @property (nonatomic) NSString *password; - - /** - The list URIs of TURN servers - including STUN servers. - The URI scheme obeys to http://tools.ietf.org/html/rfc7064#section-3.1 - and http://tools.ietf.org/html/rfc7065#section-3.1 - */ - @property (nonatomic) NSArray *uris; - - /** - Time To Live. The time is seconds this data is still valid. - It is computed by the user's homeserver when the request is made. - Then, the SDK updates the property each time it is read. - */ - @property (nonatomic) NSUInteger ttl; - - /** - The `ttl` value transcoded to an absolute date, a timestamp in milliseconds - based on the device clock. - */ - @property (nonatomic) uint64_t ttlExpirationLocalTs; -@end - - #pragma mark - Crypto /** `MXKeysUploadResponse` represents the response to /keys/upload request made by diff --git a/MatrixSDK/JSONModels/MXJSONModels.m b/MatrixSDK/JSONModels/MXJSONModels.m index e3b9f86794..4d49632642 100644 --- a/MatrixSDK/JSONModels/MXJSONModels.m +++ b/MatrixSDK/JSONModels/MXJSONModels.m @@ -1343,179 +1343,6 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary @end -#pragma mark - Voice over IP -#pragma mark - - -@implementation MXCallSessionDescription - -+ (id)modelFromJSON:(NSDictionary *)JSONDictionary -{ - MXCallSessionDescription *callSessionDescription = [[MXCallSessionDescription alloc] init]; - if (callSessionDescription) - { - MXJSONModelSetString(callSessionDescription.type, JSONDictionary[@"type"]); - MXJSONModelSetString(callSessionDescription.sdp, JSONDictionary[@"sdp"]); - } - - return callSessionDescription; -} - -@end - -@implementation MXCallInviteEventContent - -+ (id)modelFromJSON:(NSDictionary *)JSONDictionary -{ - MXCallInviteEventContent *callInviteEventContent = [[MXCallInviteEventContent alloc] init]; - if (callInviteEventContent) - { - MXJSONModelSetString(callInviteEventContent.callId, JSONDictionary[@"call_id"]); - MXJSONModelSetMXJSONModel(callInviteEventContent.offer, MXCallSessionDescription, JSONDictionary[@"offer"]); - MXJSONModelSetUInteger(callInviteEventContent.version, JSONDictionary[@"version"]); - MXJSONModelSetUInteger(callInviteEventContent.lifetime, JSONDictionary[@"lifetime"]); - } - - return callInviteEventContent; -} - -- (BOOL)isVideoCall -{ - return (NSNotFound != [self.offer.sdp rangeOfString:@"m=video"].location); -} - -@end - -@implementation MXCallCandidate - -+ (id)modelFromJSON:(NSDictionary *)JSONDictionary -{ - MXCallCandidate *callCandidate = [[MXCallCandidate alloc] init]; - if (callCandidate) - { - MXJSONModelSetString(callCandidate.sdpMid, JSONDictionary[@"sdpMid"]); - MXJSONModelSetUInteger(callCandidate.sdpMLineIndex, JSONDictionary[@"sdpMLineIndex"]); - MXJSONModelSetString(callCandidate.candidate, JSONDictionary[@"candidate"]); - } - - return callCandidate; -} - -- (NSDictionary *)JSONDictionary -{ - NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionary]; - - JSONDictionary[@"sdpMid"] = _sdpMid; - JSONDictionary[@"sdpMLineIndex"] = @(_sdpMLineIndex); - JSONDictionary[@"candidate"] = _candidate; - - return JSONDictionary; -} - -- (NSString *)description -{ - return [NSString stringWithFormat:@" %@ - %tu - %@", self, _sdpMid, _sdpMLineIndex, _candidate]; -} - -@end - -@implementation MXCallCandidatesEventContent - -+ (id)modelFromJSON:(NSDictionary *)JSONDictionary -{ - MXCallCandidatesEventContent *callCandidatesEventContent = [[MXCallCandidatesEventContent alloc] init]; - if (callCandidatesEventContent) - { - MXJSONModelSetString(callCandidatesEventContent.callId, JSONDictionary[@"call_id"]); - MXJSONModelSetUInteger(callCandidatesEventContent.version, JSONDictionary[@"version"]); - MXJSONModelSetMXJSONModelArray(callCandidatesEventContent.candidates, MXCallCandidate, JSONDictionary[@"candidates"]); - } - - return callCandidatesEventContent; -} - -@end - -@implementation MXCallAnswerEventContent - -+ (id)modelFromJSON:(NSDictionary *)JSONDictionary -{ - MXCallAnswerEventContent *callAnswerEventContent = [[MXCallAnswerEventContent alloc] init]; - if (callAnswerEventContent) - { - MXJSONModelSetString(callAnswerEventContent.callId, JSONDictionary[@"call_id"]); - MXJSONModelSetUInteger(callAnswerEventContent.version, JSONDictionary[@"version"]); - MXJSONModelSetMXJSONModel(callAnswerEventContent.answer, MXCallSessionDescription, JSONDictionary[@"answer"]); - } - - return callAnswerEventContent; -} - -@end - -@implementation MXCallHangupEventContent - -+ (id)modelFromJSON:(NSDictionary *)JSONDictionary -{ - MXCallHangupEventContent *callHangupEventContent = [[MXCallHangupEventContent alloc] init]; - if (callHangupEventContent) - { - MXJSONModelSetString(callHangupEventContent.callId, JSONDictionary[@"call_id"]); - MXJSONModelSetUInteger(callHangupEventContent.version, JSONDictionary[@"version"]); - } - - return callHangupEventContent; -} - -@end - -@implementation MXTurnServerResponse - -+ (id)modelFromJSON:(NSDictionary *)JSONDictionary -{ - MXTurnServerResponse *turnServerResponse = [[MXTurnServerResponse alloc] init]; - if (turnServerResponse) - { - MXJSONModelSetString(turnServerResponse.username, JSONDictionary[@"username"]); - MXJSONModelSetString(turnServerResponse.password, JSONDictionary[@"password"]); - MXJSONModelSetArray(turnServerResponse.uris, JSONDictionary[@"uris"]); - MXJSONModelSetUInteger(turnServerResponse.ttl, JSONDictionary[@"ttl"]); - } - - return turnServerResponse; -} - -- (instancetype)init -{ - self = [super init]; - if (self) - { - _ttlExpirationLocalTs = -1; - } - return self; -} - -- (void)setTtl:(NSUInteger)ttl -{ - if (-1 == _ttlExpirationLocalTs) - { - NSTimeInterval d = [[NSDate date] timeIntervalSince1970]; - _ttlExpirationLocalTs = (d + ttl) * 1000 ; - } -} - -- (NSUInteger)ttl -{ - NSUInteger ttl = 0; - if (-1 != _ttlExpirationLocalTs) - { - ttl = (NSUInteger)(_ttlExpirationLocalTs / 1000 - (uint64_t)[[NSDate date] timeIntervalSince1970]); - } - return ttl; -} - -@end - - #pragma mark - Crypto @implementation MXKeysUploadResponse diff --git a/MatrixSDK/MXRestClient.h b/MatrixSDK/MXRestClient.h index d03f2981a5..0c0911ec1b 100644 --- a/MatrixSDK/MXRestClient.h +++ b/MatrixSDK/MXRestClient.h @@ -40,6 +40,7 @@ #import "MXAggregationPaginatedResponse.h" #import "MXPusher.h" #import "MXRoomCreationParameters.h" +#import "MXTurnServerResponse.h" #pragma mark - Constants definitions /** diff --git a/MatrixSDK/VoIP/MXCall.h b/MatrixSDK/VoIP/MXCall.h index b21564fe6d..cae47a3cc2 100644 --- a/MatrixSDK/VoIP/MXCall.h +++ b/MatrixSDK/VoIP/MXCall.h @@ -72,11 +72,6 @@ typedef NS_ENUM(NSInteger, MXCallEndReason) */ extern NSString *const kMXCallStateDidChange; -/** - Call version - */ -extern NSString *const kMXCallVersion; - @protocol MXCallDelegate; /** diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 6414b919e1..81ce36cf9a 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -24,9 +24,12 @@ #import "MXSession.h" #import "MXTools.h" +#import "MXCallInviteEventContent.h" +#import "MXCallAnswerEventContent.h" +#import "MXCallCandidatesEventContent.h" + #pragma mark - Constants definitions NSString *const kMXCallStateDidChange = @"kMXCallStateDidChange"; -NSString *const kMXCallVersion = @"1"; @interface MXCall () { diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 4ed506ef68..9bb4d1e321 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -25,6 +25,11 @@ #import "MXSession.h" #import "MXTools.h" +#import "MXCallInviteEventContent.h" +#import "MXCallAnswerEventContent.h" +#import "MXCallHangupEventContent.h" +#import "MXCallCandidatesEventContent.h" + #pragma mark - Constants definitions NSString *const kMXCallManagerNewCall = @"kMXCallManagerNewCall"; NSString *const kMXCallManagerConferenceStarted = @"kMXCallManagerConferenceStarted"; diff --git a/MatrixSDKTests/Mocks/MXMockCallStack.h b/MatrixSDKTests/Mocks/MXMockCallStack.h index 47ae4a914c..38a066791e 100644 --- a/MatrixSDKTests/Mocks/MXMockCallStack.h +++ b/MatrixSDKTests/Mocks/MXMockCallStack.h @@ -17,7 +17,7 @@ #import #import "MXCallStack.h" - +#import "MXCallEventContent.h" /** `MXMockCallStack` is a mock implementation of the `MXOpenWebRTCCall` protocol. From 79e8be3dd30dfeed58c7be3a37fc123520cdd9c6 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 11 Nov 2020 17:33:36 +0300 Subject: [PATCH 03/89] Add invitee to call invite --- .../Call/MXCallInviteEventContent.h | 5 ++++ .../Call/MXCallInviteEventContent.m | 1 + MatrixSDK/VoIP/MXCall.m | 25 ++++++++++++------- MatrixSDK/VoIP/MXCallManager.m | 6 +++++ MatrixSDKTests/MXVoIPTests.m | 3 ++- 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.h b/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.h index 5e42358c15..ef67221f22 100644 --- a/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.h +++ b/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.h @@ -40,6 +40,11 @@ */ @property (nonatomic) NSUInteger lifetime; +/** + Target user id of the invite. Can be nil. Invites without an invitee defined to be intended for any member of the room (other than the sender). + */ +@property (nonatomic, copy) NSString *invitee; + /** Indicate whether the invitation is for a video call. */ diff --git a/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.m b/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.m index 2f9c080738..6d71292ee5 100644 --- a/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.m +++ b/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.m @@ -28,6 +28,7 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary MXJSONModelSetNumber(callInviteEventContent.versionNumber, JSONDictionary[@"version"]); MXJSONModelSetString(callInviteEventContent.versionString, JSONDictionary[@"version"]); MXJSONModelSetUInteger(callInviteEventContent.lifetime, JSONDictionary[@"lifetime"]); + MXJSONModelSetString(callInviteEventContent.invitee, JSONDictionary[@"invitee"]); } return callInviteEventContent; diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 81ce36cf9a..0ac84f34ff 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -342,15 +342,22 @@ - (void)callWithVideo:(BOOL)video NSLog(@"[MXCall] callWithVideo:%@ - Offer created: %@", (video ? @"YES" : @"NO"), sdp); // The call invite can sent to the HS - NSDictionary *content = @{ - @"call_id": self.callId, - @"offer": @{ - @"type": @"offer", - @"sdp": sdp - }, - @"version": kMXCallVersion, - @"lifetime": @(self->callManager.inviteLifetime) - }; + NSMutableDictionary *content = [@{ + @"call_id": self.callId, + @"offer": @{ + @"type": @"offer", + @"sdp": sdp + }, + @"version": kMXCallVersion, + @"lifetime": @(self->callManager.inviteLifetime) + } mutableCopy]; + + NSString *directUserId = self.room.directUserId; + if (directUserId) + { + content[@"invitee"] = directUserId; + } + [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallInvite content:content localEcho:nil success:^(NSString *eventId) { [self setState:MXCallStateInviteSent reason:nil]; diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 9bb4d1e321..1c2a065ce2 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -330,6 +330,12 @@ - (void)refreshTURNServer - (void)handleCallInvite:(MXEvent *)event { MXCallInviteEventContent *content = [MXCallInviteEventContent modelFromJSON:event.content]; + + if (content.invitee && ![_mxSession.myUserId isEqualToString:content.invitee]) + { + // this call invite targeted someone, and it's not me, ignore + return; + } // Check expiration (usefull filter when receiving load of events when resuming the event stream) if (event.age < content.lifetime) diff --git a/MatrixSDKTests/MXVoIPTests.m b/MatrixSDKTests/MXVoIPTests.m index 39324e0246..2a19ac4db9 100644 --- a/MatrixSDKTests/MXVoIPTests.m +++ b/MatrixSDKTests/MXVoIPTests.m @@ -97,7 +97,8 @@ - (void)testNoVoIPStackOnCallInvite @"sdp": @"A SDP" }, @"version": kMXCallVersion, - @"lifetime": @(30 * 1000) + @"lifetime": @(30 * 1000), + @"invitee": bobSession.myUserId }; From 7bd3a39d629346421944a77c23dcb425c8d54031 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 12 Nov 2020 01:33:46 +0300 Subject: [PATCH 04/89] Introduce party_id --- MatrixSDK.xcodeproj/project.pbxproj | 22 +++++++---- .../{ => Events}/MXCallAnswerEventContent.h | 0 .../{ => Events}/MXCallAnswerEventContent.m | 3 +- .../MXCallCandidatesEventContent.h | 0 .../MXCallCandidatesEventContent.m | 3 +- .../Call/{ => Events}/MXCallEventContent.h | 9 +++++ .../Call/{ => Events}/MXCallEventContent.m | 19 ++++++++++ .../{ => Events}/MXCallHangupEventContent.h | 0 .../{ => Events}/MXCallHangupEventContent.m | 3 +- .../{ => Events}/MXCallInviteEventContent.h | 0 .../{ => Events}/MXCallInviteEventContent.m | 3 +- MatrixSDK/VoIP/MXCall.h | 5 +++ MatrixSDK/VoIP/MXCall.m | 38 ++++++++++++++++--- MatrixSDK/VoIP/MXCallManager.m | 37 ++++++++++-------- MatrixSDKTests/MXVoIPTests.m | 3 +- 15 files changed, 108 insertions(+), 37 deletions(-) rename MatrixSDK/JSONModels/Call/{ => Events}/MXCallAnswerEventContent.h (100%) rename MatrixSDK/JSONModels/Call/{ => Events}/MXCallAnswerEventContent.m (85%) rename MatrixSDK/JSONModels/Call/{ => Events}/MXCallCandidatesEventContent.h (100%) rename MatrixSDK/JSONModels/Call/{ => Events}/MXCallCandidatesEventContent.m (85%) rename MatrixSDK/JSONModels/Call/{ => Events}/MXCallEventContent.h (84%) rename MatrixSDK/JSONModels/Call/{ => Events}/MXCallEventContent.m (71%) rename MatrixSDK/JSONModels/Call/{ => Events}/MXCallHangupEventContent.h (100%) rename MatrixSDK/JSONModels/Call/{ => Events}/MXCallHangupEventContent.m (84%) rename MatrixSDK/JSONModels/Call/{ => Events}/MXCallInviteEventContent.h (100%) rename MatrixSDK/JSONModels/Call/{ => Events}/MXCallInviteEventContent.m (88%) diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 164a9eda5a..ce0b52e1a3 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -3216,6 +3216,20 @@ sourceTree = ""; }; EC976538255BFE46000C36EF /* Call */ = { + isa = PBXGroup; + children = ( + EC9765C1255C2F39000C36EF /* Events */, + EC97656F255C00D5000C36EF /* MXCallSessionDescription.h */, + EC976570255C00D5000C36EF /* MXCallSessionDescription.m */, + EC976579255C0101000C36EF /* MXCallCandidate.h */, + EC97657A255C0101000C36EF /* MXCallCandidate.m */, + EC976583255C0127000C36EF /* MXTurnServerResponse.h */, + EC976584255C0127000C36EF /* MXTurnServerResponse.m */, + ); + path = Call; + sourceTree = ""; + }; + EC9765C1255C2F39000C36EF /* Events */ = { isa = PBXGroup; children = ( EC976539255BFED4000C36EF /* MXCallEventContent.h */, @@ -3228,14 +3242,8 @@ EC97655C255C002A000C36EF /* MXCallAnswerEventContent.m */, EC976565255C0062000C36EF /* MXCallHangupEventContent.h */, EC976566255C0062000C36EF /* MXCallHangupEventContent.m */, - EC97656F255C00D5000C36EF /* MXCallSessionDescription.h */, - EC976570255C00D5000C36EF /* MXCallSessionDescription.m */, - EC976579255C0101000C36EF /* MXCallCandidate.h */, - EC97657A255C0101000C36EF /* MXCallCandidate.m */, - EC976583255C0127000C36EF /* MXTurnServerResponse.h */, - EC976584255C0127000C36EF /* MXTurnServerResponse.m */, ); - path = Call; + path = Events; sourceTree = ""; }; F03EF4F91DF014D9009DF592 /* Media */ = { diff --git a/MatrixSDK/JSONModels/Call/MXCallAnswerEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.h similarity index 100% rename from MatrixSDK/JSONModels/Call/MXCallAnswerEventContent.h rename to MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.h diff --git a/MatrixSDK/JSONModels/Call/MXCallAnswerEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.m similarity index 85% rename from MatrixSDK/JSONModels/Call/MXCallAnswerEventContent.m rename to MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.m index 9e5d4137e1..ec0e4d9283 100644 --- a/MatrixSDK/JSONModels/Call/MXCallAnswerEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.m @@ -23,9 +23,8 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary MXCallAnswerEventContent *callAnswerEventContent = [[MXCallAnswerEventContent alloc] init]; if (callAnswerEventContent) { + [callAnswerEventContent parseJSON:JSONDictionary]; MXJSONModelSetString(callAnswerEventContent.callId, JSONDictionary[@"call_id"]); - MXJSONModelSetNumber(callAnswerEventContent.versionNumber, JSONDictionary[@"version"]); - MXJSONModelSetString(callAnswerEventContent.versionString, JSONDictionary[@"version"]); MXJSONModelSetMXJSONModel(callAnswerEventContent.answer, MXCallSessionDescription, JSONDictionary[@"answer"]); } diff --git a/MatrixSDK/JSONModels/Call/MXCallCandidatesEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.h similarity index 100% rename from MatrixSDK/JSONModels/Call/MXCallCandidatesEventContent.h rename to MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.h diff --git a/MatrixSDK/JSONModels/Call/MXCallCandidatesEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.m similarity index 85% rename from MatrixSDK/JSONModels/Call/MXCallCandidatesEventContent.m rename to MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.m index b6a4b603da..8b6dd6c03e 100644 --- a/MatrixSDK/JSONModels/Call/MXCallCandidatesEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.m @@ -23,9 +23,8 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary MXCallCandidatesEventContent *callCandidatesEventContent = [[MXCallCandidatesEventContent alloc] init]; if (callCandidatesEventContent) { + [callCandidatesEventContent parseJSON:JSONDictionary]; MXJSONModelSetString(callCandidatesEventContent.callId, JSONDictionary[@"call_id"]); - MXJSONModelSetNumber(callCandidatesEventContent.versionNumber, JSONDictionary[@"version"]); - MXJSONModelSetString(callCandidatesEventContent.versionString, JSONDictionary[@"version"]); MXJSONModelSetMXJSONModelArray(callCandidatesEventContent.candidates, MXCallCandidate, JSONDictionary[@"candidates"]); } diff --git a/MatrixSDK/JSONModels/Call/MXCallEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.h similarity index 84% rename from MatrixSDK/JSONModels/Call/MXCallEventContent.h rename to MatrixSDK/JSONModels/Call/Events/MXCallEventContent.h index 404c259a86..c6974e1c64 100644 --- a/MatrixSDK/JSONModels/Call/MXCallEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.h @@ -35,6 +35,15 @@ extern NSString *const kMXCallVersion; */ @property (nonatomic, copy) NSString *versionString; +/** + The party id for the call event. + */ +@property (nonatomic, copy) NSString *partyId; + +/// Parse base fields from the JSON +/// @param JSONDictionary The JSON to be parsed +- (void)parseJSON:(NSDictionary *)JSONDictionary; + /// Derived value from versionNumber or versionString - (NSString *)version; diff --git a/MatrixSDK/JSONModels/Call/MXCallEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m similarity index 71% rename from MatrixSDK/JSONModels/Call/MXCallEventContent.m rename to MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m index 912603e0b6..b46f0887f5 100644 --- a/MatrixSDK/JSONModels/Call/MXCallEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m @@ -33,6 +33,25 @@ + (void)initialize }); } +- (void)parseJSON:(NSDictionary *)JSONDictionary +{ + MXJSONModelSetNumber(self.versionNumber, JSONDictionary[@"version"]); + MXJSONModelSetString(self.versionString, JSONDictionary[@"version"]); + MXJSONModelSetString(self.partyId, JSONDictionary[@"party_id"]); +} + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXCallEventContent *callEventContent = [[MXCallEventContent alloc] init]; + + if (callEventContent) + { + [callEventContent parseJSON:JSONDictionary]; + } + + return callEventContent; +} + - (NSString *)version { NSString *_version; diff --git a/MatrixSDK/JSONModels/Call/MXCallHangupEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.h similarity index 100% rename from MatrixSDK/JSONModels/Call/MXCallHangupEventContent.h rename to MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.h diff --git a/MatrixSDK/JSONModels/Call/MXCallHangupEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.m similarity index 84% rename from MatrixSDK/JSONModels/Call/MXCallHangupEventContent.m rename to MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.m index 2779294e6e..e71fcd6c27 100644 --- a/MatrixSDK/JSONModels/Call/MXCallHangupEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.m @@ -23,9 +23,8 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary MXCallHangupEventContent *callHangupEventContent = [[MXCallHangupEventContent alloc] init]; if (callHangupEventContent) { + [callHangupEventContent parseJSON:JSONDictionary]; MXJSONModelSetString(callHangupEventContent.callId, JSONDictionary[@"call_id"]); - MXJSONModelSetNumber(callHangupEventContent.versionNumber, JSONDictionary[@"version"]); - MXJSONModelSetString(callHangupEventContent.versionString, JSONDictionary[@"version"]); } return callHangupEventContent; diff --git a/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.h similarity index 100% rename from MatrixSDK/JSONModels/Call/MXCallInviteEventContent.h rename to MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.h diff --git a/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.m similarity index 88% rename from MatrixSDK/JSONModels/Call/MXCallInviteEventContent.m rename to MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.m index 6d71292ee5..e7ddaefd2c 100644 --- a/MatrixSDK/JSONModels/Call/MXCallInviteEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.m @@ -23,10 +23,9 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary MXCallInviteEventContent *callInviteEventContent = [[MXCallInviteEventContent alloc] init]; if (callInviteEventContent) { + [callInviteEventContent parseJSON:JSONDictionary]; MXJSONModelSetString(callInviteEventContent.callId, JSONDictionary[@"call_id"]); MXJSONModelSetMXJSONModel(callInviteEventContent.offer, MXCallSessionDescription, JSONDictionary[@"offer"]); - MXJSONModelSetNumber(callInviteEventContent.versionNumber, JSONDictionary[@"version"]); - MXJSONModelSetString(callInviteEventContent.versionString, JSONDictionary[@"version"]); MXJSONModelSetUInteger(callInviteEventContent.lifetime, JSONDictionary[@"lifetime"]); MXJSONModelSetString(callInviteEventContent.invitee, JSONDictionary[@"invitee"]); } diff --git a/MatrixSDK/VoIP/MXCall.h b/MatrixSDK/VoIP/MXCall.h index cae47a3cc2..a8530296d3 100644 --- a/MatrixSDK/VoIP/MXCall.h +++ b/MatrixSDK/VoIP/MXCall.h @@ -183,6 +183,11 @@ extern NSString *const kMXCallStateDidChange; */ @property (readonly, nonatomic) NSString *callerId; +/** + The party id for this call. Will be generated on first access. + */ +@property (readonly, nonatomic, copy) NSString *partyId; + /** The user id of the callee. Nil for conference calls */ diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 0ac84f34ff..58059a52b9 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -83,6 +83,8 @@ @interface MXCall () @implementation MXCall +@synthesize partyId = _partyId; + - (instancetype)initWithRoomId:(NSString *)roomId andCallManager:(MXCallManager *)theCallManager { // For 1:1 call, use the room as the call signaling room @@ -149,6 +151,15 @@ - (instancetype)initWithRoomId:(NSString *)roomId callSignalingRoomId:(NSString return self; } +- (NSString *)partyId +{ + if (_partyId == nil) + { + _partyId = callManager.mxSession.myDeviceId; + } + return _partyId; +} + - (void)calleeId:(void (^)(NSString * _Nonnull))onComplete { if (calleeId) @@ -182,13 +193,15 @@ - (void)calleeId:(void (^)(NSString * _Nonnull))onComplete - (void)handleCallEvent:(MXEvent *)event { + BOOL isMyEvent = [self isMyEvent:event]; + switch (event.eventType) { case MXEventTypeCallInvite: { callInviteEventContent = [MXCallInviteEventContent modelFromJSON:event.content]; - if (![event.sender isEqualToString:_callSignalingRoom.mxSession.myUserId]) + if (!isMyEvent) { // Incoming call @@ -296,7 +309,7 @@ - (void)handleCallEvent:(MXEvent *)event case MXEventTypeCallCandidates: { - if (NO == [event.sender isEqualToString:_callSignalingRoom.mxSession.myUserId]) + if (!isMyEvent) { MXCallCandidatesEventContent *content = [MXCallCandidatesEventContent modelFromJSON:event.content]; @@ -349,7 +362,8 @@ - (void)callWithVideo:(BOOL)video @"sdp": sdp }, @"version": kMXCallVersion, - @"lifetime": @(self->callManager.inviteLifetime) + @"lifetime": @(self->callManager.inviteLifetime), + @"party_id": self.partyId } mutableCopy]; NSString *directUserId = self.room.directUserId; @@ -418,6 +432,7 @@ - (void)answer @"sdp": sdpAnswer }, @"version": kMXCallVersion, + @"party_id": self.partyId }; [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallAnswer content:content localEcho:nil success:nil failure:^(NSError *error) { NSLog(@"[MXCall] answer: ERROR: Cannot send m.call.answer event."); @@ -462,7 +477,8 @@ - (void)hangup // Send the hangup event NSDictionary *content = @{ @"call_id": _callId, - @"version": kMXCallVersion + @"version": kMXCallVersion, + @"party_id": self.partyId }; [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallHangup content:content localEcho:nil success:nil failure:^(NSError *error) { NSLog(@"[MXCall] hangup: ERROR: Cannot send m.call.hangup event."); @@ -649,7 +665,8 @@ - (void)sendLocalIceCandidates NSDictionary *content = @{ @"version": kMXCallVersion, @"call_id": _callId, - @"candidates": localICECandidates + @"candidates": localICECandidates, + @"party_id": self.partyId }; [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallCandidates content:content localEcho:nil success:nil failure:^(NSError *error) { @@ -675,6 +692,17 @@ - (void)callStackCallDidConnect:(id)callStackCall #pragma mark - Private methods + +- (BOOL)isMyEvent:(MXEvent *)event +{ + if ([event.sender isEqualToString:_callSignalingRoom.mxSession.myUserId]) + { + MXCallEventContent *content = [MXCallEventContent modelFromJSON:event.content]; + return [content.partyId isEqualToString:_callSignalingRoom.mxSession.myDeviceId]; + } + return NO; +} + - (NSString *)description { return [NSString stringWithFormat:@" id: %@ - isVideoCall: %@ - isIncoming: %@ - state: %@", self, _callId, @(_isVideoCall), @(_isIncoming), @(_state)]; diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 1c2a065ce2..dfba59fb64 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -333,35 +333,40 @@ - (void)handleCallInvite:(MXEvent *)event if (content.invitee && ![_mxSession.myUserId isEqualToString:content.invitee]) { - // this call invite targeted someone, and it's not me, ignore + // this call invite has a specific target, and it's not me, ignore return; } // Check expiration (usefull filter when receiving load of events when resuming the event stream) if (event.age < content.lifetime) { + if ([event.sender isEqualToString:_mxSession.myUserId] && + [content.partyId isEqualToString:_mxSession.myDeviceId]) + { + // this is a remote echo, ignore + return; + } + // If it is an invite from the peer, we need to create the MXCall - if (![event.sender isEqualToString:_mxSession.myUserId]) + + MXCall *call = [self callWithCallId:content.callId]; + if (!call) { - MXCall *call = [self callWithCallId:content.callId]; - if (!call) + call = [[MXCall alloc] initWithRoomId:event.roomId andCallManager:self]; + if (call) { - call = [[MXCall alloc] initWithRoomId:event.roomId andCallManager:self]; - if (call) - { - [calls addObject:call]; - - [call handleCallEvent:event]; + [calls addObject:call]; - // Broadcast the incoming call - [self notifyCallInvite:call.callId]; - } - } - else - { [call handleCallEvent:event]; + + // Broadcast the incoming call + [self notifyCallInvite:call.callId]; } } + else + { + [call handleCallEvent:event]; + } } } diff --git a/MatrixSDKTests/MXVoIPTests.m b/MatrixSDKTests/MXVoIPTests.m index 2a19ac4db9..40ddb2f767 100644 --- a/MatrixSDKTests/MXVoIPTests.m +++ b/MatrixSDKTests/MXVoIPTests.m @@ -98,7 +98,8 @@ - (void)testNoVoIPStackOnCallInvite }, @"version": kMXCallVersion, @"lifetime": @(30 * 1000), - @"invitee": bobSession.myUserId + @"invitee": bobSession.myUserId, + @"party_id": bobSession.myDeviceId }; From dd06cd6fb1c684e4d73b266997f20ffb41c73cc9 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 12 Nov 2020 13:42:03 +0300 Subject: [PATCH 05/89] Introduce select answer --- MatrixSDK.xcodeproj/project.pbxproj | 12 +++ .../Contrib/Swift/JSONModels/MXEvent.swift | 4 +- .../Events/MXCallSelectAnswerEventContent.h | 35 ++++++++ .../Events/MXCallSelectAnswerEventContent.m | 34 ++++++++ MatrixSDK/JSONModels/MXEvent.h | 2 + MatrixSDK/JSONModels/MXEvent.m | 1 + MatrixSDK/VoIP/MXCall.m | 81 ++++++++++++++++--- MatrixSDK/VoIP/MXCallManager.m | 15 ++++ 8 files changed, 174 insertions(+), 10 deletions(-) create mode 100644 MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.h create mode 100644 MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.m diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index ce0b52e1a3..d0cf10d22f 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -1173,6 +1173,10 @@ EC976586255C0127000C36EF /* MXTurnServerResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976583255C0127000C36EF /* MXTurnServerResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC976587255C0127000C36EF /* MXTurnServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = EC976584255C0127000C36EF /* MXTurnServerResponse.m */; }; EC976588255C0127000C36EF /* MXTurnServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = EC976584255C0127000C36EF /* MXTurnServerResponse.m */; }; + EC9765C7255C9F8A000C36EF /* MXCallSelectAnswerEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC9765C6255C9F8A000C36EF /* MXCallSelectAnswerEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC9765C8255C9F8A000C36EF /* MXCallSelectAnswerEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC9765C6255C9F8A000C36EF /* MXCallSelectAnswerEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC9765DE255C9F94000C36EF /* MXCallSelectAnswerEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC9765DD255C9F94000C36EF /* MXCallSelectAnswerEventContent.m */; }; + EC9765DF255C9F94000C36EF /* MXCallSelectAnswerEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC9765DD255C9F94000C36EF /* MXCallSelectAnswerEventContent.m */; }; ECAE7AEC24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = ECAE7AEB24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m */; }; F0173EAC1FCF0E8900B5F6A3 /* MXGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = F0173EAA1FCF0E8800B5F6A3 /* MXGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; F0173EAD1FCF0E8900B5F6A3 /* MXGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = F0173EAB1FCF0E8900B5F6A3 /* MXGroup.m */; }; @@ -1840,6 +1844,8 @@ EC97657A255C0101000C36EF /* MXCallCandidate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCallCandidate.m; sourceTree = ""; }; EC976583255C0127000C36EF /* MXTurnServerResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXTurnServerResponse.h; sourceTree = ""; }; EC976584255C0127000C36EF /* MXTurnServerResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXTurnServerResponse.m; sourceTree = ""; }; + EC9765C6255C9F8A000C36EF /* MXCallSelectAnswerEventContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCallSelectAnswerEventContent.h; sourceTree = ""; }; + EC9765DD255C9F94000C36EF /* MXCallSelectAnswerEventContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCallSelectAnswerEventContent.m; sourceTree = ""; }; ECAE7AEB24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTTPAdditionalHeadersTests.m; sourceTree = ""; }; ED2F344856EFFCA383E37B22 /* Pods-SDK-MatrixSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDK-MatrixSDK.release.xcconfig"; path = "Target Support Files/Pods-SDK-MatrixSDK/Pods-SDK-MatrixSDK.release.xcconfig"; sourceTree = ""; }; EDC74874AB2D86EFEE912B04 /* Pods-MatrixSDK-MatrixSDK-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixSDK-MatrixSDK-macOS.debug.xcconfig"; path = "Target Support Files/Pods-MatrixSDK-MatrixSDK-macOS/Pods-MatrixSDK-MatrixSDK-macOS.debug.xcconfig"; sourceTree = ""; }; @@ -3240,6 +3246,8 @@ EC976552255BFFCB000C36EF /* MXCallCandidatesEventContent.m */, EC97655B255C002A000C36EF /* MXCallAnswerEventContent.h */, EC97655C255C002A000C36EF /* MXCallAnswerEventContent.m */, + EC9765C6255C9F8A000C36EF /* MXCallSelectAnswerEventContent.h */, + EC9765DD255C9F94000C36EF /* MXCallSelectAnswerEventContent.m */, EC976565255C0062000C36EF /* MXCallHangupEventContent.h */, EC976566255C0062000C36EF /* MXCallHangupEventContent.m */, ); @@ -3383,6 +3391,7 @@ B172857C2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.h in Headers */, 329FB1791A0A74B100A5E88E /* MXTools.h in Headers */, 322691321E5EF77D00966A6E /* MXDeviceListOperation.h in Headers */, + EC9765C7255C9F8A000C36EF /* MXCallSelectAnswerEventContent.h in Headers */, 32481A841C03572900782AD3 /* MXRoomAccountData.h in Headers */, 327E9AE12285497100A98BC1 /* MXEventContentRelatesTo.h in Headers */, 32133025228BFA800070BA9B /* MXReactionCountChangeListener.h in Headers */, @@ -3775,6 +3784,7 @@ B14EF33E2397E90400758AF0 /* MXOlmInboundGroupSession.h in Headers */, B14EF33F2397E90400758AF0 /* (null) in Headers */, B14EF3402397E90400758AF0 /* MXKeyBackupVersionTrust.h in Headers */, + EC9765C8255C9F8A000C36EF /* MXCallSelectAnswerEventContent.h in Headers */, EC97654A255BFF35000C36EF /* MXCallInviteEventContent.h in Headers */, 320B3935239FA56900BE2C06 /* MXKeyVerificationByDMRequest.h in Headers */, B14EF3412397E90400758AF0 /* MXOutgoingRoomKeyRequestManager.h in Headers */, @@ -4284,6 +4294,7 @@ 32A151491DAF7C0C00400192 /* MXKey.m in Sources */, 324DD2AE246AEB7B00377005 /* MXSecretStoragePassphrase.m in Sources */, B1136964230AC9D900E2B2FA /* MXIdentityServerRestClient.m in Sources */, + EC9765DE255C9F94000C36EF /* MXCallSelectAnswerEventContent.m in Sources */, F0C34CBB1C18C93700C36F09 /* MXSDKOptions.m in Sources */, 320BBF441D6C81550079890E /* MXEventsEnumeratorOnArray.m in Sources */, 32618E7220ED2DF500E1D2EA /* MXFilterJSONModel.m in Sources */, @@ -4546,6 +4557,7 @@ 327C3E4E23A39D91006183D1 /* MXAggregatedReferencesUpdater.m in Sources */, B14EF2352397E90400758AF0 /* MXRoomPowerLevels.swift in Sources */, 32AF9287240EA2430008A0FD /* MXSecretShareRequest.m in Sources */, + EC9765DF255C9F94000C36EF /* MXCallSelectAnswerEventContent.m in Sources */, B14EF2362397E90400758AF0 /* MXAggregations.m in Sources */, B14EF2372397E90400758AF0 /* MXKeyVerificationManager.m in Sources */, B14EF2382397E90400758AF0 /* MXEvent.m in Sources */, diff --git a/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift b/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift index a058107a29..714f401d70 100644 --- a/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift +++ b/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift @@ -54,6 +54,7 @@ public enum MXEventType: Equatable, Hashable { case callInvite case callCandidates case callAnswer + case callSelectAnswer case callHangup case reaction case receipt @@ -94,6 +95,7 @@ public enum MXEventType: Equatable, Hashable { case .callInvite: return kMXEventTypeStringCallInvite case .callCandidates: return kMXEventTypeStringCallCandidates case .callAnswer: return kMXEventTypeStringCallAnswer + case .callSelectAnswer: return kMXEventTypeStringCallSelectAnswer case .callHangup: return kMXEventTypeStringCallHangup case .reaction: return kMXEventTypeStringReaction case .receipt: return kMXEventTypeStringReceipt @@ -114,7 +116,7 @@ public enum MXEventType: Equatable, Hashable { } public init(identifier: String) { - let events: [MXEventType] = [.roomName, .roomTopic, .roomAvatar, .roomMember, .roomCreate, .roomJoinRules, .roomPowerLevels, .roomAliases, .roomCanonicalAlias, .roomEncrypted, .roomEncryption, .roomGuestAccess, .roomHistoryVisibility, .roomKey, .roomForwardedKey, .roomKeyRequest, .roomMessage, .roomMessageFeedback, .roomRedaction, .roomThirdPartyInvite, .roomTag, .presence, .typing, .callInvite, .callCandidates, .callAnswer, .callHangup, .receipt, .roomTombStone] + let events: [MXEventType] = [.roomName, .roomTopic, .roomAvatar, .roomMember, .roomCreate, .roomJoinRules, .roomPowerLevels, .roomAliases, .roomCanonicalAlias, .roomEncrypted, .roomEncryption, .roomGuestAccess, .roomHistoryVisibility, .roomKey, .roomForwardedKey, .roomKeyRequest, .roomMessage, .roomMessageFeedback, .roomRedaction, .roomThirdPartyInvite, .roomTag, .presence, .typing, .callInvite, .callCandidates, .callAnswer, .callSelectAnswer, .callHangup, .receipt, .roomTombStone] self = events.first(where: { $0.identifier == identifier }) ?? .custom(identifier) } } diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.h new file mode 100644 index 0000000000..ab77ccf5d9 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.h @@ -0,0 +1,35 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "MXCallEventContent.h" + +/** + `MXCallSelectAnswerEventContent` represents the content of a m.call.select_answer event. + */ +@interface MXCallSelectAnswerEventContent : MXCallEventContent + +/** + A unique identifier for the call. + */ +@property (nonatomic) NSString *callId; + +/** + The selected party id. + */ +@property (nonatomic) NSString *selectedPartyId; + +@end diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.m new file mode 100644 index 0000000000..09c41d24fa --- /dev/null +++ b/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.m @@ -0,0 +1,34 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXCallSelectAnswerEventContent.h" + +@implementation MXCallSelectAnswerEventContent + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXCallSelectAnswerEventContent *callSelectAnswerEventContent = [[MXCallSelectAnswerEventContent alloc] init]; + if (callSelectAnswerEventContent) + { + [callSelectAnswerEventContent parseJSON:JSONDictionary]; + MXJSONModelSetString(callSelectAnswerEventContent.callId, JSONDictionary[@"call_id"]); + MXJSONModelSetString(callSelectAnswerEventContent.selectedPartyId, JSONDictionary[@"selected_party_id"]); + } + + return callSelectAnswerEventContent; +} + +@end diff --git a/MatrixSDK/JSONModels/MXEvent.h b/MatrixSDK/JSONModels/MXEvent.h index ce7630de0e..33ed93f671 100644 --- a/MatrixSDK/JSONModels/MXEvent.h +++ b/MatrixSDK/JSONModels/MXEvent.h @@ -68,6 +68,7 @@ typedef NS_ENUM(NSInteger, MXEventType) MXEventTypeCallInvite, MXEventTypeCallCandidates, MXEventTypeCallAnswer, + MXEventTypeCallSelectAnswer, MXEventTypeCallHangup, MXEventTypeSticker, MXEventTypeRoomTombStone, @@ -126,6 +127,7 @@ FOUNDATION_EXPORT NSString *const kMXEventTypeStringReadMarker; FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallInvite; FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallCandidates; FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallAnswer; +FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallSelectAnswer; FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallHangup; FOUNDATION_EXPORT NSString *const kMXEventTypeStringSticker; FOUNDATION_EXPORT NSString *const kMXEventTypeStringRoomTombStone; diff --git a/MatrixSDK/JSONModels/MXEvent.m b/MatrixSDK/JSONModels/MXEvent.m index b66384533c..8fb3d62e16 100644 --- a/MatrixSDK/JSONModels/MXEvent.m +++ b/MatrixSDK/JSONModels/MXEvent.m @@ -59,6 +59,7 @@ NSString *const kMXEventTypeStringCallInvite = @"m.call.invite"; NSString *const kMXEventTypeStringCallCandidates = @"m.call.candidates"; NSString *const kMXEventTypeStringCallAnswer = @"m.call.answer"; +NSString *const kMXEventTypeStringCallSelectAnswer = @"m.call.select_answer"; NSString *const kMXEventTypeStringCallHangup = @"m.call.hangup"; NSString *const kMXEventTypeStringSticker = @"m.sticker"; NSString *const kMXEventTypeStringRoomTombStone = @"m.room.tombstone"; diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 58059a52b9..0b0ee1a71f 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -26,6 +26,7 @@ #import "MXCallInviteEventContent.h" #import "MXCallAnswerEventContent.h" +#import "MXCallSelectAnswerEventContent.h" #import "MXCallCandidatesEventContent.h" #pragma mark - Constants definitions @@ -77,6 +78,11 @@ @interface MXCall () Cache for self.calleeId. */ NSString *calleeId; + + /** + Selected answer for this call. Only exists for outgoing calls and after an answer (an accept or reject) received. + */ + MXCallEventContent *selectedAnswer; } @end @@ -270,6 +276,12 @@ - (void)handleCallEvent:(MXEvent *)event // Listen to answer event only for call we are making, not receiving if (!_isIncoming) { + if (selectedAnswer) + { + // there is already a selected answer, ignore this one + return; + } + // MXCall receives this event only when it placed a call MXCallAnswerEventContent *content = [MXCallAnswerEventContent modelFromJSON:event.content]; @@ -279,15 +291,48 @@ - (void)handleCallEvent:(MXEvent *)event [inviteExpirationTimer invalidate]; inviteExpirationTimer = nil; } - - // Let's the stack finalise the connection - [self setState:MXCallStateConnecting reason:event]; - [callStackCall handleAnswer:content.answer.sdp - success:^{} - failure:^(NSError *error) { - NSLog(@"[MXCall] handleCallEvent: ERROR: Cannot send handle answer. Error: %@\nEvent: %@", error, event); - [self didEncounterError:error]; - }]; + + // mark this as the selected one + selectedAnswer = content; + + void(^continueBlock)(void) = ^{ + // Let's the stack finalise the connection + [self setState:MXCallStateConnecting reason:event]; + [self->callStackCall handleAnswer:content.answer.sdp + success:^{} + failure:^(NSError *error) { + NSLog(@"[MXCall] handleCallEvent: ERROR: Cannot send handle answer. Error: %@\nEvent: %@", error, event); + self->selectedAnswer = nil; + [self didEncounterError:error]; + }]; + }; + + if ([content.version isEqualToString:kMXCallVersion]) + { + NSDictionary *selectAnswerContent = @{ + @"call_id": self.callId, + @"version": kMXCallVersion, + @"party_id": self.partyId, + @"selected_party_id": content.partyId + }; + + [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallSelectAnswer + content:selectAnswerContent + localEcho:nil + success:^(NSString *eventId) { + + continueBlock(); + + } failure:^(NSError *error) { + NSLog(@"[MXCall] callWithVideo: ERROR: Cannot send m.call.select_answer event. Error: %@\n", error); + self->selectedAnswer = nil; + [self didEncounterError:error]; + }]; + } + else + { + continueBlock(); + } } else if (_state == MXCallStateRinging) { @@ -298,6 +343,24 @@ - (void)handleCallEvent:(MXEvent *)event break; } + case MXEventTypeCallSelectAnswer: + { + if (!isMyEvent) + { + if (_isIncoming) + { + MXCallSelectAnswerEventContent *content = [MXCallSelectAnswerEventContent modelFromJSON:event.content]; + if (![content.selectedPartyId isEqualToString:self.partyId]) + { + // Else this event means that the call has been answered by the user from + // another device + [self onCallAnsweredElsewhere]; + } + } + } + break; + } + case MXEventTypeCallHangup: { if (_state != MXCallStateEnded) diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index dfba59fb64..5f237efea7 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -27,6 +27,7 @@ #import "MXCallInviteEventContent.h" #import "MXCallAnswerEventContent.h" +#import "MXCallSelectAnswerEventContent.h" #import "MXCallHangupEventContent.h" #import "MXCallCandidatesEventContent.h" @@ -278,6 +279,9 @@ - (void)handleCallEvent:(MXEvent *)event case MXEventTypeCallAnswer: [self handleCallAnswer:event]; break; + case MXEventTypeCallSelectAnswer: + [self handleCallSelectAnswer:event]; + break; case MXEventTypeCallHangup: [self handleCallHangup:event]; break; @@ -408,6 +412,17 @@ - (void)handleCallAnswer:(MXEvent *)event } } +- (void)handleCallSelectAnswer:(MXEvent *)event +{ + MXCallSelectAnswerEventContent *content = [MXCallSelectAnswerEventContent modelFromJSON:event.content]; + + MXCall *call = [self callWithCallId:content.callId]; + if (call) + { + [call handleCallEvent:event]; + } +} + - (void)handleCallHangup:(MXEvent *)event { MXCallHangupEventContent *content = [MXCallHangupEventContent modelFromJSON:event.content]; From 31930c5fccb245cdbdc477ffb04787e2a0c81a38 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 12 Nov 2020 16:03:00 +0300 Subject: [PATCH 06/89] Introduce call reject --- MatrixSDK.xcodeproj/project.pbxproj | 12 ++ .../Contrib/Swift/JSONModels/MXEvent.swift | 4 +- .../Call/Events/MXCallRejectEventContent.h | 30 +++++ .../Call/Events/MXCallRejectEventContent.m | 33 ++++++ MatrixSDK/JSONModels/MXEvent.h | 2 + MatrixSDK/JSONModels/MXEvent.m | 1 + MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m | 1 + MatrixSDK/VoIP/MXCall.h | 1 + MatrixSDK/VoIP/MXCall.m | 105 ++++++++++++++++-- MatrixSDK/VoIP/MXCallManager.m | 16 +++ 10 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.h create mode 100644 MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.m diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index d0cf10d22f..86b3bb7656 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -1177,6 +1177,10 @@ EC9765C8255C9F8A000C36EF /* MXCallSelectAnswerEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC9765C6255C9F8A000C36EF /* MXCallSelectAnswerEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC9765DE255C9F94000C36EF /* MXCallSelectAnswerEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC9765DD255C9F94000C36EF /* MXCallSelectAnswerEventContent.m */; }; EC9765DF255C9F94000C36EF /* MXCallSelectAnswerEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC9765DD255C9F94000C36EF /* MXCallSelectAnswerEventContent.m */; }; + EC976605255D496D000C36EF /* MXCallRejectEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976604255D496C000C36EF /* MXCallRejectEventContent.h */; }; + EC976606255D496D000C36EF /* MXCallRejectEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976604255D496C000C36EF /* MXCallRejectEventContent.h */; }; + EC976610255D4988000C36EF /* MXCallRejectEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC97660F255D4988000C36EF /* MXCallRejectEventContent.m */; }; + EC976611255D4988000C36EF /* MXCallRejectEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC97660F255D4988000C36EF /* MXCallRejectEventContent.m */; }; ECAE7AEC24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = ECAE7AEB24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m */; }; F0173EAC1FCF0E8900B5F6A3 /* MXGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = F0173EAA1FCF0E8800B5F6A3 /* MXGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; F0173EAD1FCF0E8900B5F6A3 /* MXGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = F0173EAB1FCF0E8900B5F6A3 /* MXGroup.m */; }; @@ -1846,6 +1850,8 @@ EC976584255C0127000C36EF /* MXTurnServerResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXTurnServerResponse.m; sourceTree = ""; }; EC9765C6255C9F8A000C36EF /* MXCallSelectAnswerEventContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCallSelectAnswerEventContent.h; sourceTree = ""; }; EC9765DD255C9F94000C36EF /* MXCallSelectAnswerEventContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCallSelectAnswerEventContent.m; sourceTree = ""; }; + EC976604255D496C000C36EF /* MXCallRejectEventContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCallRejectEventContent.h; sourceTree = ""; }; + EC97660F255D4988000C36EF /* MXCallRejectEventContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCallRejectEventContent.m; sourceTree = ""; }; ECAE7AEB24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTTPAdditionalHeadersTests.m; sourceTree = ""; }; ED2F344856EFFCA383E37B22 /* Pods-SDK-MatrixSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDK-MatrixSDK.release.xcconfig"; path = "Target Support Files/Pods-SDK-MatrixSDK/Pods-SDK-MatrixSDK.release.xcconfig"; sourceTree = ""; }; EDC74874AB2D86EFEE912B04 /* Pods-MatrixSDK-MatrixSDK-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixSDK-MatrixSDK-macOS.debug.xcconfig"; path = "Target Support Files/Pods-MatrixSDK-MatrixSDK-macOS/Pods-MatrixSDK-MatrixSDK-macOS.debug.xcconfig"; sourceTree = ""; }; @@ -3250,6 +3256,8 @@ EC9765DD255C9F94000C36EF /* MXCallSelectAnswerEventContent.m */, EC976565255C0062000C36EF /* MXCallHangupEventContent.h */, EC976566255C0062000C36EF /* MXCallHangupEventContent.m */, + EC976604255D496C000C36EF /* MXCallRejectEventContent.h */, + EC97660F255D4988000C36EF /* MXCallRejectEventContent.m */, ); path = Events; sourceTree = ""; @@ -3320,6 +3328,7 @@ 32CE6FB81A409B1F00317F1E /* MXFileStoreMetaData.h in Headers */, B19A30C82404268600FB6F35 /* MXQRCodeDataBuilder.h in Headers */, 32A31BC820D401FC005916C7 /* MXRoomFilter.h in Headers */, + EC976605255D496D000C36EF /* MXCallRejectEventContent.h in Headers */, 324AAC74239913AD00380A66 /* MXKeyVerificationDone.h in Headers */, B146D47421A5945800D8C2C6 /* MXAntivirusScanStatus.h in Headers */, 322691361E5EFF8700966A6E /* MXDeviceListOperationsPool.h in Headers */, @@ -3591,6 +3600,7 @@ B14EF2AC2397E90400758AF0 /* MXMemoryStore.h in Headers */, B14EF2AD2397E90400758AF0 /* MXEventAnnotationChunk.h in Headers */, B14EF2AE2397E90400758AF0 /* (null) in Headers */, + EC976606255D496D000C36EF /* MXCallRejectEventContent.h in Headers */, EC976568255C0062000C36EF /* MXCallHangupEventContent.h in Headers */, B14EF2AF2397E90400758AF0 /* MXWellKnown.h in Headers */, B14EF2B02397E90400758AF0 /* MXRecoveryKey.h in Headers */, @@ -4216,6 +4226,7 @@ B146D4F221A5AF7F00D8C2C6 /* MXRealmEventScanMapper.m in Sources */, B11BD45A22CB58850064D8B0 /* MXReplyEventFormattedBodyParts.m in Sources */, 32CE6FB91A409B1F00317F1E /* MXFileStoreMetaData.m in Sources */, + EC976610255D4988000C36EF /* MXCallRejectEventContent.m in Sources */, 3264DB921CEC528D00B99881 /* MXAccountData.m in Sources */, 3213301A228B010C0070BA9B /* MXRealmReactionCount.m in Sources */, 3250E7CB220C913900736CB5 /* MXCryptoTools.m in Sources */, @@ -4440,6 +4451,7 @@ B14EF1D72397E90400758AF0 /* MXMediaLoader.m in Sources */, EC976560255C002A000C36EF /* MXCallAnswerEventContent.m in Sources */, 32549AF823F2E2790002576B /* MXKeyVerificationReady.m in Sources */, + EC976611255D4988000C36EF /* MXCallRejectEventContent.m in Sources */, B14EF1D82397E90400758AF0 /* MXMegolmBackupAuthData.m in Sources */, B14EF1D92397E90400758AF0 /* MXReactionCount.m in Sources */, B14EF1DA2397E90400758AF0 /* MXRecoveryKey.m in Sources */, diff --git a/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift b/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift index 714f401d70..135bca24dd 100644 --- a/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift +++ b/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift @@ -56,6 +56,7 @@ public enum MXEventType: Equatable, Hashable { case callAnswer case callSelectAnswer case callHangup + case callReject case reaction case receipt case roomTombStone @@ -97,6 +98,7 @@ public enum MXEventType: Equatable, Hashable { case .callAnswer: return kMXEventTypeStringCallAnswer case .callSelectAnswer: return kMXEventTypeStringCallSelectAnswer case .callHangup: return kMXEventTypeStringCallHangup + case .callReject: return kMXEventTypeStringCallReject case .reaction: return kMXEventTypeStringReaction case .receipt: return kMXEventTypeStringReceipt case .roomTombStone: return kMXEventTypeStringRoomTombStone @@ -116,7 +118,7 @@ public enum MXEventType: Equatable, Hashable { } public init(identifier: String) { - let events: [MXEventType] = [.roomName, .roomTopic, .roomAvatar, .roomMember, .roomCreate, .roomJoinRules, .roomPowerLevels, .roomAliases, .roomCanonicalAlias, .roomEncrypted, .roomEncryption, .roomGuestAccess, .roomHistoryVisibility, .roomKey, .roomForwardedKey, .roomKeyRequest, .roomMessage, .roomMessageFeedback, .roomRedaction, .roomThirdPartyInvite, .roomTag, .presence, .typing, .callInvite, .callCandidates, .callAnswer, .callSelectAnswer, .callHangup, .receipt, .roomTombStone] + let events: [MXEventType] = [.roomName, .roomTopic, .roomAvatar, .roomMember, .roomCreate, .roomJoinRules, .roomPowerLevels, .roomAliases, .roomCanonicalAlias, .roomEncrypted, .roomEncryption, .roomGuestAccess, .roomHistoryVisibility, .roomKey, .roomForwardedKey, .roomKeyRequest, .roomMessage, .roomMessageFeedback, .roomRedaction, .roomThirdPartyInvite, .roomTag, .presence, .typing, .callInvite, .callCandidates, .callAnswer, .callSelectAnswer, .callHangup, .callReject, .receipt, .roomTombStone] self = events.first(where: { $0.identifier == identifier }) ?? .custom(identifier) } } diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.h new file mode 100644 index 0000000000..e801ad6300 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.h @@ -0,0 +1,30 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "MXCallEventContent.h" + +/** + `MXCallRejectEventContent` represents the content of a m.call.reject event. + */ +@interface MXCallRejectEventContent : MXCallEventContent + +/** + A unique identifier for the call. + */ +@property (nonatomic) NSString *callId; + +@end diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.m new file mode 100644 index 0000000000..9b0f21742d --- /dev/null +++ b/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.m @@ -0,0 +1,33 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXCallRejectEventContent.h" + +@implementation MXCallRejectEventContent + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXCallRejectEventContent *callRejectEventContent = [[MXCallRejectEventContent alloc] init]; + if (callRejectEventContent) + { + [callRejectEventContent parseJSON:JSONDictionary]; + MXJSONModelSetString(callRejectEventContent.callId, JSONDictionary[@"call_id"]); + } + + return callRejectEventContent; +} + +@end diff --git a/MatrixSDK/JSONModels/MXEvent.h b/MatrixSDK/JSONModels/MXEvent.h index 33ed93f671..48d68fc8a5 100644 --- a/MatrixSDK/JSONModels/MXEvent.h +++ b/MatrixSDK/JSONModels/MXEvent.h @@ -70,6 +70,7 @@ typedef NS_ENUM(NSInteger, MXEventType) MXEventTypeCallAnswer, MXEventTypeCallSelectAnswer, MXEventTypeCallHangup, + MXEventTypeCallReject, MXEventTypeSticker, MXEventTypeRoomTombStone, MXEventTypeKeyVerificationRequest, @@ -129,6 +130,7 @@ FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallCandidates; FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallAnswer; FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallSelectAnswer; FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallHangup; +FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallReject; FOUNDATION_EXPORT NSString *const kMXEventTypeStringSticker; FOUNDATION_EXPORT NSString *const kMXEventTypeStringRoomTombStone; diff --git a/MatrixSDK/JSONModels/MXEvent.m b/MatrixSDK/JSONModels/MXEvent.m index 8fb3d62e16..dbf02c2826 100644 --- a/MatrixSDK/JSONModels/MXEvent.m +++ b/MatrixSDK/JSONModels/MXEvent.m @@ -61,6 +61,7 @@ NSString *const kMXEventTypeStringCallAnswer = @"m.call.answer"; NSString *const kMXEventTypeStringCallSelectAnswer = @"m.call.select_answer"; NSString *const kMXEventTypeStringCallHangup = @"m.call.hangup"; +NSString *const kMXEventTypeStringCallReject = @"m.call.reject"; NSString *const kMXEventTypeStringSticker = @"m.sticker"; NSString *const kMXEventTypeStringRoomTombStone = @"m.room.tombstone"; NSString *const kMXEventTypeStringKeyVerificationRequest= @"m.key.verification.request"; diff --git a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m index 1b8f13dcd4..bf73ca293a 100644 --- a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m +++ b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m @@ -140,6 +140,7 @@ - (void)endCall:(MXCall *)call; switch (endReason) { case MXCallEndReasonRemoteHangup: + case MXCallEndReasonRejected: reason = CXCallEndedReasonRemoteEnded; break; case MXCallEndReasonHangupElsewhere: diff --git a/MatrixSDK/VoIP/MXCall.h b/MatrixSDK/VoIP/MXCall.h index a8530296d3..98a85fb5f9 100644 --- a/MatrixSDK/VoIP/MXCall.h +++ b/MatrixSDK/VoIP/MXCall.h @@ -61,6 +61,7 @@ typedef NS_ENUM(NSInteger, MXCallEndReason) MXCallEndReasonHangup, // The call was ended by the local side MXCallEndReasonHangupElsewhere, // The call was ended on another device MXCallEndReasonRemoteHangup, // The call was ended by the remote side + MXCallEndReasonRejected, // The call was explicitly rejected MXCallEndReasonBusy, // The call was declined by the remote side before it was being established. Only for outgoing calls MXCallEndReasonMissed, // The call wasn't established in a given period of time MXCallEndReasonAnsweredElseWhere // The call was answered on another device diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 0b0ee1a71f..cfcb7e270e 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -28,6 +28,7 @@ #import "MXCallAnswerEventContent.h" #import "MXCallSelectAnswerEventContent.h" #import "MXCallCandidatesEventContent.h" +#import "MXCallRejectEventContent.h" #pragma mark - Constants definitions NSString *const kMXCallStateDidChange = @"kMXCallStateDidChange"; @@ -352,8 +353,7 @@ - (void)handleCallEvent:(MXEvent *)event MXCallSelectAnswerEventContent *content = [MXCallSelectAnswerEventContent modelFromJSON:event.content]; if (![content.selectedPartyId isEqualToString:self.partyId]) { - // Else this event means that the call has been answered by the user from - // another device + // Else this event means that the call has been answered (accepted/rejected) by another user/device [self onCallAnsweredElsewhere]; } } @@ -385,6 +385,54 @@ - (void)handleCallEvent:(MXEvent *)event break; } + case MXEventTypeCallReject: + { + // Listen to answer event only for call we are making, not receiving + if (!_isIncoming) + { + if (selectedAnswer) + { + // there is already a selected answer, ignore this one + return; + } + + // The peer rejected our outgoing call + if (inviteExpirationTimer) + { + [inviteExpirationTimer invalidate]; + inviteExpirationTimer = nil; + } + + if (_state != MXCallStateEnded) + { + MXCallRejectEventContent *content = [MXCallRejectEventContent modelFromJSON:event.content]; + selectedAnswer = content; + + NSDictionary *selectAnswerContent = @{ + @"call_id": self.callId, + @"version": kMXCallVersion, + @"party_id": self.partyId, + @"selected_party_id": content.partyId + }; + + [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallSelectAnswer + content:selectAnswerContent + localEcho:nil + success:^(NSString *eventId) { + + [self terminateWithReason:event]; + + } failure:^(NSError *error) { + NSLog(@"[MXCall] callWithVideo: ERROR: Cannot send m.call.select_answer event. Error: %@\n", error); + self->selectedAnswer = nil; + [self didEncounterError:error]; + }]; + } + } + + break; + } + default: break; } @@ -532,6 +580,24 @@ - (void)answer - (void)hangup { NSLog(@"[MXCall] hangup"); + + if (self.state == MXCallStateRinging && [callInviteEventContent.version isEqualToString:kMXCallVersion]) + { + // Send the reject event for new call invites + NSDictionary *content = @{ + @"call_id": _callId, + @"version": kMXCallVersion, + @"party_id": self.partyId + }; + MXEvent *localEcho; + [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallReject content:content localEcho:&localEcho success:nil failure:^(NSError *error) { + NSLog(@"[MXCall] hangup: ERROR: Cannot send m.call.reject event."); + [self didEncounterError:error]; + }]; + + [self terminateWithReason:localEcho]; + return; + } if (self.state != MXCallStateEnded) { @@ -789,12 +855,35 @@ - (void)terminateWithReason:(MXEvent *)event // Determine call end reason if (event) { - if ([event.sender isEqualToString:callManager.mxSession.myUserId]) - _endReason = MXCallEndReasonHangupElsewhere; - else if (!self.isEstablished && !self.isIncoming) - _endReason = MXCallEndReasonBusy; - else - _endReason = MXCallEndReasonRemoteHangup; + switch (event.eventType) + { + case MXEventTypeCallHangup: + { + if ([event.sender isEqualToString:callManager.mxSession.myUserId]) + { + _endReason = MXCallEndReasonHangupElsewhere; + } + else if (!self.isEstablished && !self.isIncoming) + { + _endReason = MXCallEndReasonBusy; + } + else + { + _endReason = MXCallEndReasonRemoteHangup; + } + break; + } + case MXEventTypeCallReject: + { + _endReason = MXCallEndReasonRejected; + break; + } + default: + { + _endReason = MXCallEndReasonHangup; + break; + } + } } else { diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 5f237efea7..f2fc01f92f 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -30,6 +30,7 @@ #import "MXCallSelectAnswerEventContent.h" #import "MXCallHangupEventContent.h" #import "MXCallCandidatesEventContent.h" +#import "MXCallRejectEventContent.h" #pragma mark - Constants definitions NSString *const kMXCallManagerNewCall = @"kMXCallManagerNewCall"; @@ -288,6 +289,9 @@ - (void)handleCallEvent:(MXEvent *)event case MXEventTypeCallCandidates: [self handleCallCandidates:event]; break; + case MXEventTypeCallReject: + [self handleCallReject:event]; + break; default: break; } @@ -450,6 +454,18 @@ - (void)handleCallCandidates:(MXEvent *)event } } +- (void)handleCallReject:(MXEvent *)event +{ + MXCallRejectEventContent *content = [MXCallRejectEventContent modelFromJSON:event.content]; + + // Forward the event to the MXCall object + MXCall *call = [self callWithCallId:content.callId]; + if (call) + { + [call handleCallEvent:event]; + } +} + - (void)handleCallStateDidChangeNotification:(NSNotification *)notification { #if TARGET_OS_IPHONE From cdd2469795dbdea6bea0534932190bee701cfff0 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 12 Nov 2020 16:54:41 +0300 Subject: [PATCH 07/89] Fixes --- MatrixSDK/MXSession.m | 2 ++ MatrixSDK/Utils/MXTools.m | 2 ++ MatrixSDK/VoIP/MXCall.m | 11 ++++++++--- MatrixSDK/VoIP/MXCallManager.m | 4 +++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/MatrixSDK/MXSession.m b/MatrixSDK/MXSession.m index 57171c4ab8..c33e6743f7 100644 --- a/MatrixSDK/MXSession.m +++ b/MatrixSDK/MXSession.m @@ -231,7 +231,9 @@ - (id)initWithMatrixRestClient:(MXRestClient*)mxRestClient kMXEventTypeStringCallInvite, kMXEventTypeStringCallCandidates, kMXEventTypeStringCallAnswer, + kMXEventTypeStringCallSelectAnswer, kMXEventTypeStringCallHangup, + kMXEventTypeStringCallReject, kMXEventTypeStringSticker ]; diff --git a/MatrixSDK/Utils/MXTools.m b/MatrixSDK/Utils/MXTools.m index 4a1530a286..30ec663175 100644 --- a/MatrixSDK/Utils/MXTools.m +++ b/MatrixSDK/Utils/MXTools.m @@ -102,7 +102,9 @@ + (void)initialize kMXEventTypeStringCallInvite, kMXEventTypeStringCallCandidates, kMXEventTypeStringCallAnswer, + kMXEventTypeStringCallSelectAnswer, kMXEventTypeStringCallHangup, + kMXEventTypeStringCallReject, kMXEventTypeStringSticker, kMXEventTypeStringRoomTombStone, kMXEventTypeStringKeyVerificationRequest, diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index cfcb7e270e..cc7d4288de 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -589,13 +589,18 @@ - (void)hangup @"version": kMXCallVersion, @"party_id": self.partyId }; - MXEvent *localEcho; - [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallReject content:content localEcho:&localEcho success:nil failure:^(NSError *error) { + MXEvent *fakeEvent = [MXEvent modelFromJSON:@{ + @"type": kMXEventTypeStringCallReject, + @"content": content + }]; + [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallReject content:content localEcho:nil success:^(NSString *eventId) { + + } failure:^(NSError *error) { NSLog(@"[MXCall] hangup: ERROR: Cannot send m.call.reject event."); [self didEncounterError:error]; }]; - [self terminateWithReason:localEcho]; + [self terminateWithReason:fakeEvent]; return; } diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index f2fc01f92f..433620d25e 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -81,7 +81,9 @@ - (instancetype)initWithMatrixSession:(MXSession *)mxSession andCallStack:(id Date: Fri, 13 Nov 2020 17:39:28 +0300 Subject: [PATCH 08/89] Introduce new call hangup reasons --- .../Swift/JSONModels/MXJSONModels.swift | 33 +++++ .../Call/Events/MXCallHangupEventContent.h | 32 ++++- .../Call/Events/MXCallHangupEventContent.m | 23 +++ MatrixSDK/Utils/MXTools.h | 4 + MatrixSDK/Utils/MXTools.m | 62 ++++++++ MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h | 1 + MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m | 19 ++- MatrixSDK/VoIP/MXCall.h | 11 +- MatrixSDK/VoIP/MXCall.m | 132 ++++++++++++------ 9 files changed, 262 insertions(+), 55 deletions(-) diff --git a/MatrixSDK/Contrib/Swift/JSONModels/MXJSONModels.swift b/MatrixSDK/Contrib/Swift/JSONModels/MXJSONModels.swift index 3493a7a762..e6e645a41f 100644 --- a/MatrixSDK/Contrib/Swift/JSONModels/MXJSONModels.swift +++ b/MatrixSDK/Contrib/Swift/JSONModels/MXJSONModels.swift @@ -112,3 +112,36 @@ public enum MXPushRuleScope: Equatable, Hashable { self = scopes.first(where: { $0.identifier == identifier }) ?? .device(profileTag: identifier) } } + +/// Hangup reason definitions +public enum MXCallHangupReason: Equatable, Hashable { + case userHangup + case iceFailed + case inviteTimeout + case iceTimeout + case userMediaFailed + case unknownError + + public var identifier: String { + switch self { + case .userHangup: + return kMXCallHangupReasonUserHangup + case .iceFailed: + return kMXCallHangupReasonIceFailed + case .inviteTimeout: + return kMXCallHangupReasonInviteTimeout + case .iceTimeout: + return kMXCallHangupReasonIceTimeout + case .userMediaFailed: + return kMXCallHangupReasonUserMediaFailed + case .unknownError: + return kMXCallHangupReasonUnknownError + } + } + + public init(identifier: String) { + let reasons: [MXCallHangupReason] = [.userHangup, .iceFailed, .inviteTimeout, .iceTimeout, .userMediaFailed, .unknownError] + self = reasons.first(where: { $0.identifier == identifier }) ?? .userHangup + } + +} diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.h index 5af70b4501..f3b19c2e76 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.h @@ -17,6 +17,25 @@ #import #import "MXCallEventContent.h" +typedef NS_ENUM(NSInteger, MXCallHangupReason) +{ + MXCallHangupReasonUserHangup, + MXCallHangupReasonIceFailed, + MXCallHangupReasonInviteTimeout, + MXCallHangupReasonIceTimeout, + MXCallHangupReasonUserMediaFailed, + MXCallHangupReasonUnknownError +} NS_REFINED_FOR_SWIFT; + +typedef NSString * MXCallHangupReasonString NS_REFINED_FOR_SWIFT; + +FOUNDATION_EXPORT NSString *const kMXCallHangupReasonUserHangup; +FOUNDATION_EXPORT NSString *const kMXCallHangupReasonIceFailed; +FOUNDATION_EXPORT NSString *const kMXCallHangupReasonInviteTimeout; +FOUNDATION_EXPORT NSString *const kMXCallHangupReasonIceTimeout; +FOUNDATION_EXPORT NSString *const kMXCallHangupReasonUserMediaFailed; +FOUNDATION_EXPORT NSString *const kMXCallHangupReasonUnknownError; + /** `MXCallHangupEventContent` represents the content of a m.call.hangup event. */ @@ -25,6 +44,17 @@ /** A unique identifier for the call. */ -@property (nonatomic) NSString *callId; +@property (nonatomic, copy) NSString *callId; + +/** + The reason of the hangup event. Can be mapped to a MXCallHangupReason enum. + @seealso reasonType + */ +@property (nonatomic, copy) MXCallHangupReasonString reason; + +/** + Mapped reason of the hangup event. + */ +@property (nonatomic, assign) MXCallHangupReason reasonType; @end diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.m index e71fcd6c27..366ce765f3 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.m @@ -15,6 +15,14 @@ // #import "MXCallHangupEventContent.h" +#import "MXTools.h" + +NSString *const kMXCallHangupReasonUserHangup = @"user_hangup"; +NSString *const kMXCallHangupReasonIceFailed = @"ice_failed"; +NSString *const kMXCallHangupReasonInviteTimeout = @"invite_timeout"; +NSString *const kMXCallHangupReasonIceTimeout = @"ice_timeout"; +NSString *const kMXCallHangupReasonUserMediaFailed = @"user_media_failed"; +NSString *const kMXCallHangupReasonUnknownError = @"unknown_error"; @implementation MXCallHangupEventContent @@ -25,9 +33,24 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary { [callHangupEventContent parseJSON:JSONDictionary]; MXJSONModelSetString(callHangupEventContent.callId, JSONDictionary[@"call_id"]); + MXJSONModelSetString(callHangupEventContent.reason, JSONDictionary[@"reason"]); + if (!callHangupEventContent.reason) + { + callHangupEventContent.reason = kMXCallHangupReasonUserHangup; + } } return callHangupEventContent; } +- (MXCallHangupReason)reasonType +{ + return [MXTools callHangupReason:self.reason]; +} + +- (void)setReasonType:(MXCallHangupReason)reasonType +{ + self.reason = [MXTools callHangupReasonString:reasonType]; +} + @end diff --git a/MatrixSDK/Utils/MXTools.h b/MatrixSDK/Utils/MXTools.h index 8c6e108dd8..f5c2dc2694 100644 --- a/MatrixSDK/Utils/MXTools.h +++ b/MatrixSDK/Utils/MXTools.h @@ -26,6 +26,7 @@ #import "MXEvent.h" #import "MXJSONModels.h" #import "MXEnumConstants.h" +#import "MXCallHangupEventContent.h" @interface MXTools : NSObject @@ -38,6 +39,9 @@ + (MXPresence)presence:(MXPresenceString)presenceString; + (MXPresenceString)presenceString:(MXPresence)presence; ++ (MXCallHangupReason)callHangupReason:(MXCallHangupReasonString)reasonString; ++ (MXCallHangupReasonString)callHangupReasonString:(MXCallHangupReason)reason; + /** Generate a random secret key. diff --git a/MatrixSDK/Utils/MXTools.m b/MatrixSDK/Utils/MXTools.m index 30ec663175..34bd57abc4 100644 --- a/MatrixSDK/Utils/MXTools.m +++ b/MatrixSDK/Utils/MXTools.m @@ -277,6 +277,68 @@ + (MXPresenceString)presenceString:(MXPresence)presence return presenceString; } ++ (MXCallHangupReason)callHangupReason:(MXCallHangupReasonString)reasonString +{ + MXCallHangupReason reason = MXCallHangupReasonUserHangup; + + if ([reasonString isEqualToString:kMXCallHangupReasonUserHangup]) + { + reason = MXCallHangupReasonUserHangup; + } + else if ([reasonString isEqualToString:kMXCallHangupReasonIceFailed]) + { + reason = MXCallHangupReasonIceFailed; + } + else if ([reasonString isEqualToString:kMXCallHangupReasonInviteTimeout]) + { + reason = MXCallHangupReasonInviteTimeout; + } + else if ([reasonString isEqualToString:kMXCallHangupReasonIceTimeout]) + { + reason = MXCallHangupReasonIceTimeout; + } + else if ([reasonString isEqualToString:kMXCallHangupReasonUserMediaFailed]) + { + reason = MXCallHangupReasonUserMediaFailed; + } + else if ([reasonString isEqualToString:kMXCallHangupReasonUnknownError]) + { + reason = MXCallHangupReasonUnknownError; + } + + return reason; +} + ++ (MXCallHangupReasonString)callHangupReasonString:(MXCallHangupReason)reason +{ + MXCallHangupReasonString string; + + switch (reason) { + case MXCallHangupReasonUserHangup: + string = kMXCallHangupReasonUserHangup; + break; + case MXCallHangupReasonIceFailed: + string = kMXCallHangupReasonIceFailed; + break; + case MXCallHangupReasonInviteTimeout: + string = kMXCallHangupReasonInviteTimeout; + break; + case MXCallHangupReasonIceTimeout: + string = kMXCallHangupReasonIceTimeout; + break; + case MXCallHangupReasonUserMediaFailed: + string = kMXCallHangupReasonUserMediaFailed; + break; + case MXCallHangupReasonUnknownError: + string = kMXCallHangupReasonUnknownError; + break; + default: + break; + } + + return string; +} + + (NSString *)generateSecret { return [[NSProcessInfo processInfo] globallyUniqueString]; diff --git a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h index 7d17fed7de..206cfb8184 100644 --- a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h +++ b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h @@ -29,6 +29,7 @@ NS_ASSUME_NONNULL_BEGIN */ extern NSString * const kMXCallKitAdapterAudioSessionDidActive; +API_AVAILABLE(ios(10.0)) @interface MXCallKitAdapter : NSObject /** diff --git a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m index bf73ca293a..fa1f610bb9 100644 --- a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m +++ b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m @@ -140,13 +140,12 @@ - (void)endCall:(MXCall *)call; switch (endReason) { case MXCallEndReasonRemoteHangup: - case MXCallEndReasonRejected: + case MXCallEndReasonBusy: reason = CXCallEndedReasonRemoteEnded; break; case MXCallEndReasonHangupElsewhere: reason = CXCallEndedReasonDeclinedElsewhere; break; - case MXCallEndReasonBusy: case MXCallEndReasonMissed: reason = CXCallEndedReasonUnanswered; break; @@ -203,7 +202,7 @@ - (void)reportIncomingCall:(MXCall *)call { [self.provider reportNewIncomingCallWithUUID:callUUID update:update completion:^(NSError * _Nullable error) { if (error) { - [call hangup]; + [call hangupWithReason:MXCallHangupReasonUnknownError]; [self.calls removeObjectForKey:callUUID]; return; } @@ -223,14 +222,14 @@ - (void)reportCall:(MXCall *)call connectedAtDate:(nullable NSDate *)date + (BOOL)callKitAvailable { - if (@available(iOS 10.0, *)) { - // CallKit currently illegal in China - // https://github.com/vector-im/riot-ios/issues/1941 - - return ![NSLocale.currentLocale.countryCode isEqual: @"CN"]; - } +#if TARGET_IPHONE_SIMULATOR + return NO; +#endif + + // CallKit currently illegal in China + // https://github.com/vector-im/riot-ios/issues/1941 - return NO; + return ![NSLocale.currentLocale.countryCode isEqualToString:@"CN"]; } #pragma mark - CXProviderDelegate diff --git a/MatrixSDK/VoIP/MXCall.h b/MatrixSDK/VoIP/MXCall.h index 98a85fb5f9..7e985133db 100644 --- a/MatrixSDK/VoIP/MXCall.h +++ b/MatrixSDK/VoIP/MXCall.h @@ -23,6 +23,7 @@ #endif #import "MXCallStackCall.h" +#import "MXCallHangupEventContent.h" NS_ASSUME_NONNULL_BEGIN @@ -61,8 +62,7 @@ typedef NS_ENUM(NSInteger, MXCallEndReason) MXCallEndReasonHangup, // The call was ended by the local side MXCallEndReasonHangupElsewhere, // The call was ended on another device MXCallEndReasonRemoteHangup, // The call was ended by the remote side - MXCallEndReasonRejected, // The call was explicitly rejected - MXCallEndReasonBusy, // The call was declined by the remote side before it was being established. Only for outgoing calls + MXCallEndReasonBusy, // The call was declined by the local/remote side before it was being established. MXCallEndReasonMissed, // The call wasn't established in a given period of time MXCallEndReasonAnsweredElseWhere // The call was answered on another device }; @@ -125,6 +125,10 @@ extern NSString *const kMXCallStateDidChange; */ - (void)hangup; +/** + Hang up a call with a reason in progress. + */ +- (void)hangupWithReason:(MXCallHangupReason)reason; #pragma mark - Properties /** @@ -279,8 +283,9 @@ extern NSString *const kMXCallStateDidChange; @param call the instance that changes. @param error the error. + @param reason The hangup reason, which would be sent if this method was not implemented. */ -- (void)call:(MXCall *)call didEncounterError:(NSError *)error; +- (void)call:(MXCall *)call didEncounterError:(NSError *)error reason:(MXCallHangupReason)reason; @end diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index cc7d4288de..6d07d9b1aa 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -248,11 +248,11 @@ - (void)handleCallEvent:(MXEvent *)event } failure:^(NSError * _Nonnull error) { NSLog(@"[MXCall] handleOffer: ERROR: Couldn't handle offer. Error: %@", error); - [self didEncounterError:error]; + [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; }]; } failure:^(NSError *error) { NSLog(@"[MXCall] startCapturingMediaWithVideo: ERROR: Couldn't start capturing. Error: %@", error); - [self didEncounterError:error]; + [self didEncounterError:error reason:MXCallHangupReasonUserMediaFailed]; }]; } else @@ -304,7 +304,7 @@ - (void)handleCallEvent:(MXEvent *)event failure:^(NSError *error) { NSLog(@"[MXCall] handleCallEvent: ERROR: Cannot send handle answer. Error: %@\nEvent: %@", error, event); self->selectedAnswer = nil; - [self didEncounterError:error]; + [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; }]; }; @@ -327,7 +327,7 @@ - (void)handleCallEvent:(MXEvent *)event } failure:^(NSError *error) { NSLog(@"[MXCall] callWithVideo: ERROR: Cannot send m.call.select_answer event. Error: %@\n", error); self->selectedAnswer = nil; - [self didEncounterError:error]; + [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; } else @@ -425,7 +425,7 @@ - (void)handleCallEvent:(MXEvent *)event } failure:^(NSError *error) { NSLog(@"[MXCall] callWithVideo: ERROR: Cannot send m.call.select_answer event. Error: %@\n", error); self->selectedAnswer = nil; - [self didEncounterError:error]; + [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; } } @@ -489,16 +489,16 @@ - (void)callWithVideo:(BOOL)video } failure:^(NSError *error) { NSLog(@"[MXCall] callWithVideo: ERROR: Cannot send m.call.invite event."); - [self didEncounterError:error]; + [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; } failure:^(NSError *error) { NSLog(@"[MXCall] callWithVideo: ERROR: Cannot create offer. Error: %@", error); - [self didEncounterError:error]; + [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; }]; } failure:^(NSError *error) { NSLog(@"[MXCall] callWithVideo: ERROR: Cannot start capturing media. Error: %@", error); - [self didEncounterError:error]; + [self didEncounterError:error reason:MXCallHangupReasonUserMediaFailed]; }]; } @@ -547,12 +547,12 @@ - (void)answer }; [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallAnswer content:content localEcho:nil success:nil failure:^(NSError *error) { NSLog(@"[MXCall] answer: ERROR: Cannot send m.call.answer event."); - [self didEncounterError:error]; + [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; } failure:^(NSError *error) { NSLog(@"[MXCall] answer: ERROR: Cannot create offer. Error: %@", error); - [self didEncounterError:error]; + [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; }]; self->callInviteEventContent = nil; @@ -567,7 +567,7 @@ - (void)answer NSLog(@"[MXCall] answer: ensuring encryption is ready to use ..."); [callManager.mxSession.crypto ensureEncryptionInRoom:_callSignalingRoom.roomId success:answer failure:^(NSError *error) { NSLog(@"[MXCall] answer: ERROR: [MXCrypto ensureEncryptionInRoom] failed. Error: %@", error); - [self didEncounterError:error]; + [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; } else @@ -589,39 +589,54 @@ - (void)hangup @"version": kMXCallVersion, @"party_id": self.partyId }; + + [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallReject content:content localEcho:nil success:nil failure:^(NSError *error) { + NSLog(@"[MXCall] hangup: ERROR: Cannot send m.call.reject event."); + [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; + }]; + + // terminate with a fake reject event MXEvent *fakeEvent = [MXEvent modelFromJSON:@{ @"type": kMXEventTypeStringCallReject, @"content": content }]; - [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallReject content:content localEcho:nil success:^(NSString *eventId) { - - } failure:^(NSError *error) { - NSLog(@"[MXCall] hangup: ERROR: Cannot send m.call.reject event."); - [self didEncounterError:error]; - }]; - + fakeEvent.sender = callManager.mxSession.myUserId; [self terminateWithReason:fakeEvent]; return; } + // hangup with the default reason + [self hangupWithReason:MXCallHangupReasonUserHangup]; +} + +- (void)hangupWithReason:(MXCallHangupReason)reason +{ + NSLog(@"[MXCall] hangupWithReason: %ld", (long)reason); + if (self.state != MXCallStateEnded) { - [self terminateWithReason:nil]; - // Send the hangup event NSDictionary *content = @{ @"call_id": _callId, @"version": kMXCallVersion, - @"party_id": self.partyId + @"party_id": self.partyId, + @"reason": [MXTools callHangupReasonString:reason] }; [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallHangup content:content localEcho:nil success:nil failure:^(NSError *error) { NSLog(@"[MXCall] hangup: ERROR: Cannot send m.call.hangup event."); - [self didEncounterError:error]; + [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; + + // terminate with a fake hangup event + MXEvent *fakeEvent = [MXEvent modelFromJSON:@{ + @"type": kMXEventTypeStringCallHangup, + @"content": content + }]; + fakeEvent.sender = callManager.mxSession.myUserId; + [self terminateWithReason:fakeEvent]; } } - #pragma marl - Properties - (void)setState:(MXCallState)state reason:(MXEvent *)event { @@ -805,7 +820,7 @@ - (void)sendLocalIceCandidates [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallCandidates content:content localEcho:nil success:nil failure:^(NSError *error) { NSLog(@"[MXCall] onICECandidate: Warning: Cannot send m.call.candidates event."); - [self didEncounterError:error]; + [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; [localICECandidates removeAllObjects]; @@ -815,13 +830,23 @@ - (void)sendLocalIceCandidates - (void)callStackCall:(id)callStackCall onError:(NSError *)error { NSLog(@"[MXCall] callStackCall didEncounterError: %@", error); - [self didEncounterError:error]; + + if (self.isEstablished) + { + [self didEncounterError:error reason:MXCallHangupReasonIceTimeout]; + } + else + { + [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; + } } - (void)callStackCallDidConnect:(id)callStackCall { if (self.state == MXCallStateConnecting) + { [self setState:MXCallStateConnected reason:nil]; + } } @@ -864,23 +889,46 @@ - (void)terminateWithReason:(MXEvent *)event { case MXEventTypeCallHangup: { - if ([event.sender isEqualToString:callManager.mxSession.myUserId]) - { - _endReason = MXCallEndReasonHangupElsewhere; - } - else if (!self.isEstablished && !self.isIncoming) - { - _endReason = MXCallEndReasonBusy; - } - else - { - _endReason = MXCallEndReasonRemoteHangup; + MXCallHangupEventContent *content = [MXCallHangupEventContent modelFromJSON:event.content]; + MXCallHangupReason reason = content.reasonType; + + switch (reason) { + case MXCallHangupReasonUserHangup: + if ([event.sender isEqualToString:callManager.mxSession.myUserId]) + { + if ([content.partyId isEqualToString:self.partyId]) + { + _endReason = MXCallEndReasonHangup; + } + else + { + _endReason = MXCallEndReasonHangupElsewhere; + } + } + else if (!self.isEstablished && !self.isIncoming) + { + _endReason = MXCallEndReasonBusy; + } + else + { + _endReason = MXCallEndReasonRemoteHangup; + } + break; + case MXCallHangupReasonIceFailed: + case MXCallHangupReasonIceTimeout: + case MXCallHangupReasonUserMediaFailed: + case MXCallHangupReasonUnknownError: + _endReason = MXCallEndReasonUnknown; + break; + case MXCallHangupReasonInviteTimeout: + _endReason = MXCallEndReasonMissed; + break; } break; } case MXEventTypeCallReject: { - _endReason = MXCallEndReasonRejected; + _endReason = MXCallEndReasonBusy; break; } default: @@ -894,19 +942,21 @@ - (void)terminateWithReason:(MXEvent *)event { _endReason = MXCallEndReasonHangup; } + + NSLog(@"[MXCall] terminateWithReason: %@, endReason: %ld", event, (long)_endReason); [self setState:MXCallStateEnded reason:event]; } -- (void)didEncounterError:(NSError *)error +- (void)didEncounterError:(NSError *)error reason:(MXCallHangupReason)reason { - if ([_delegate respondsToSelector:@selector(call:didEncounterError:)]) + if ([_delegate respondsToSelector:@selector(call:didEncounterError:reason:)]) { - [_delegate call:self didEncounterError:error]; + [_delegate call:self didEncounterError:error reason:reason]; } else { - [self hangup]; + [self hangupWithReason:reason]; } } From 1486a14956803bbda4ff243f78cd28eeafccb1b5 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 16 Nov 2020 15:51:37 +0300 Subject: [PATCH 09/89] Introduce negotiate event --- MatrixSDK.xcodeproj/project.pbxproj | 16 ++++++- .../Contrib/Swift/JSONModels/MXEvent.swift | 4 +- .../Call/Events/MXCallNegotiateEventContent.h | 48 +++++++++++++++++++ .../Call/Events/MXCallNegotiateEventContent.m | 40 ++++++++++++++++ MatrixSDK/JSONModels/MXEvent.h | 2 + MatrixSDK/JSONModels/MXEvent.m | 1 + MatrixSDK/VoIP/MXCall.m | 24 ++++++++-- MatrixSDK/VoIP/MXCallManager.m | 29 ++++++++++- 8 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h create mode 100644 MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 86b3bb7656..5180bdb060 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -1177,11 +1177,15 @@ EC9765C8255C9F8A000C36EF /* MXCallSelectAnswerEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC9765C6255C9F8A000C36EF /* MXCallSelectAnswerEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC9765DE255C9F94000C36EF /* MXCallSelectAnswerEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC9765DD255C9F94000C36EF /* MXCallSelectAnswerEventContent.m */; }; EC9765DF255C9F94000C36EF /* MXCallSelectAnswerEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC9765DD255C9F94000C36EF /* MXCallSelectAnswerEventContent.m */; }; - EC976605255D496D000C36EF /* MXCallRejectEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976604255D496C000C36EF /* MXCallRejectEventContent.h */; }; - EC976606255D496D000C36EF /* MXCallRejectEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976604255D496C000C36EF /* MXCallRejectEventContent.h */; }; + EC976605255D496D000C36EF /* MXCallRejectEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976604255D496C000C36EF /* MXCallRejectEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC976606255D496D000C36EF /* MXCallRejectEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976604255D496C000C36EF /* MXCallRejectEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC976610255D4988000C36EF /* MXCallRejectEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC97660F255D4988000C36EF /* MXCallRejectEventContent.m */; }; EC976611255D4988000C36EF /* MXCallRejectEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC97660F255D4988000C36EF /* MXCallRejectEventContent.m */; }; ECAE7AEC24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = ECAE7AEB24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m */; }; + ECFFA970255ED32700706454 /* MXCallNegotiateEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = ECFFA96F255ED32700706454 /* MXCallNegotiateEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ECFFA971255ED32700706454 /* MXCallNegotiateEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = ECFFA96F255ED32700706454 /* MXCallNegotiateEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ECFFA977255ED34500706454 /* MXCallNegotiateEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = ECFFA976255ED34500706454 /* MXCallNegotiateEventContent.m */; }; + ECFFA978255ED34500706454 /* MXCallNegotiateEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = ECFFA976255ED34500706454 /* MXCallNegotiateEventContent.m */; }; F0173EAC1FCF0E8900B5F6A3 /* MXGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = F0173EAA1FCF0E8800B5F6A3 /* MXGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; F0173EAD1FCF0E8900B5F6A3 /* MXGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = F0173EAB1FCF0E8900B5F6A3 /* MXGroup.m */; }; F03EF4FE1DF014D9009DF592 /* MXMediaLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = F03EF4FA1DF014D9009DF592 /* MXMediaLoader.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1853,6 +1857,8 @@ EC976604255D496C000C36EF /* MXCallRejectEventContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCallRejectEventContent.h; sourceTree = ""; }; EC97660F255D4988000C36EF /* MXCallRejectEventContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCallRejectEventContent.m; sourceTree = ""; }; ECAE7AEB24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTTPAdditionalHeadersTests.m; sourceTree = ""; }; + ECFFA96F255ED32700706454 /* MXCallNegotiateEventContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCallNegotiateEventContent.h; sourceTree = ""; }; + ECFFA976255ED34500706454 /* MXCallNegotiateEventContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCallNegotiateEventContent.m; sourceTree = ""; }; ED2F344856EFFCA383E37B22 /* Pods-SDK-MatrixSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDK-MatrixSDK.release.xcconfig"; path = "Target Support Files/Pods-SDK-MatrixSDK/Pods-SDK-MatrixSDK.release.xcconfig"; sourceTree = ""; }; EDC74874AB2D86EFEE912B04 /* Pods-MatrixSDK-MatrixSDK-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixSDK-MatrixSDK-macOS.debug.xcconfig"; path = "Target Support Files/Pods-MatrixSDK-MatrixSDK-macOS/Pods-MatrixSDK-MatrixSDK-macOS.debug.xcconfig"; sourceTree = ""; }; F0173EAA1FCF0E8800B5F6A3 /* MXGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXGroup.h; sourceTree = ""; }; @@ -3258,6 +3264,8 @@ EC976566255C0062000C36EF /* MXCallHangupEventContent.m */, EC976604255D496C000C36EF /* MXCallRejectEventContent.h */, EC97660F255D4988000C36EF /* MXCallRejectEventContent.m */, + ECFFA96F255ED32700706454 /* MXCallNegotiateEventContent.h */, + ECFFA976255ED34500706454 /* MXCallNegotiateEventContent.m */, ); path = Events; sourceTree = ""; @@ -3388,6 +3396,7 @@ 320BBF3C1D6C7D9D0079890E /* MXEventsEnumerator.h in Headers */, 32637ED41E5B00400011E20D /* MXDeviceList.h in Headers */, 32F9FA7D1DBA0CF0009D98A6 /* MXDecryptionResult.h in Headers */, + ECFFA970255ED32700706454 /* MXCallNegotiateEventContent.h in Headers */, 3245A7501AF7B2930001D8A7 /* MXCall.h in Headers */, 02CAD43B217DD12F0074700B /* MXContentScanEncryptedBody.h in Headers */, 32D2CC0423422462002BD8CA /* MX3PidAddManager.h in Headers */, @@ -3726,6 +3735,7 @@ B14EF30C2397E90400758AF0 /* MXDeviceInfo.h in Headers */, 32B0E34023A378320054FF1A /* MXEventReference.h in Headers */, EC97655E255C002A000C36EF /* MXCallAnswerEventContent.h in Headers */, + ECFFA971255ED32700706454 /* MXCallNegotiateEventContent.h in Headers */, 324AAC802399143400380A66 /* MXKeyVerificationJSONModel.h in Headers */, B14EF30D2397E90400758AF0 /* MXEventRelations.h in Headers */, B14EF30E2397E90400758AF0 /* MXPushRuleRoomMemberCountConditionChecker.h in Headers */, @@ -4116,6 +4126,7 @@ 32CEEF4B23B0A8170039BA98 /* MXCrossSigningTools.m in Sources */, 320B3936239FA56900BE2C06 /* MXKeyVerificationByDMRequest.m in Sources */, B146D47121A5939100D8C2C6 /* MXRealmHelper.m in Sources */, + ECFFA977255ED34500706454 /* MXCallNegotiateEventContent.m in Sources */, 32261B8C23C74A230018F1E2 /* MXDeviceTrustLevel.m in Sources */, 327E9AD82284803100A98BC1 /* MXEventAnnotation.m in Sources */, 32999DE022DCD183004FF987 /* MXPusher.m in Sources */, @@ -4598,6 +4609,7 @@ B14EF24B2397E90400758AF0 /* MXServiceTermsRestClient.m in Sources */, B14EF24C2397E90400758AF0 /* MXCredentials.m in Sources */, B14EF24D2397E90400758AF0 /* MXOutgoingRoomKeyRequest.m in Sources */, + ECFFA978255ED34500706454 /* MXCallNegotiateEventContent.m in Sources */, B14EF24E2397E90400758AF0 /* MXAllowedCertificates.m in Sources */, B14EF24F2397E90400758AF0 /* MXLRUCache.m in Sources */, EC97657E255C0101000C36EF /* MXCallCandidate.m in Sources */, diff --git a/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift b/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift index 135bca24dd..9aae3b488c 100644 --- a/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift +++ b/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift @@ -57,6 +57,7 @@ public enum MXEventType: Equatable, Hashable { case callSelectAnswer case callHangup case callReject + case callNegotiate case reaction case receipt case roomTombStone @@ -99,6 +100,7 @@ public enum MXEventType: Equatable, Hashable { case .callSelectAnswer: return kMXEventTypeStringCallSelectAnswer case .callHangup: return kMXEventTypeStringCallHangup case .callReject: return kMXEventTypeStringCallReject + case .callNegotiate: return kMXEventTypeStringCallNegotiate case .reaction: return kMXEventTypeStringReaction case .receipt: return kMXEventTypeStringReceipt case .roomTombStone: return kMXEventTypeStringRoomTombStone @@ -118,7 +120,7 @@ public enum MXEventType: Equatable, Hashable { } public init(identifier: String) { - let events: [MXEventType] = [.roomName, .roomTopic, .roomAvatar, .roomMember, .roomCreate, .roomJoinRules, .roomPowerLevels, .roomAliases, .roomCanonicalAlias, .roomEncrypted, .roomEncryption, .roomGuestAccess, .roomHistoryVisibility, .roomKey, .roomForwardedKey, .roomKeyRequest, .roomMessage, .roomMessageFeedback, .roomRedaction, .roomThirdPartyInvite, .roomTag, .presence, .typing, .callInvite, .callCandidates, .callAnswer, .callSelectAnswer, .callHangup, .callReject, .receipt, .roomTombStone] + let events: [MXEventType] = [.roomName, .roomTopic, .roomAvatar, .roomMember, .roomCreate, .roomJoinRules, .roomPowerLevels, .roomAliases, .roomCanonicalAlias, .roomEncrypted, .roomEncryption, .roomGuestAccess, .roomHistoryVisibility, .roomKey, .roomForwardedKey, .roomKeyRequest, .roomMessage, .roomMessageFeedback, .roomRedaction, .roomThirdPartyInvite, .roomTag, .presence, .typing, .callInvite, .callCandidates, .callAnswer, .callSelectAnswer, .callHangup, .callReject, .callNegotiate, .receipt, .roomTombStone] self = events.first(where: { $0.identifier == identifier }) ?? .custom(identifier) } } diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h new file mode 100644 index 0000000000..bbeda752a0 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h @@ -0,0 +1,48 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "MXCallEventContent.h" +#import "MXCallSessionDescription.h" + +/** + `MXCallNegotiateEventContent` represents the content of a m.call.negotiate event. + */ +@interface MXCallNegotiateEventContent : MXCallEventContent + +/** + A unique identifier for the call. + */ +@property (nonatomic) NSString *callId; + +/** + The session description. + */ +@property (nonatomic) MXCallSessionDescription *offer; + +/** + The time in milliseconds that the invite is valid for. + Once the invite age exceeds this value, clients should discard it. + They should also no longer show the call as awaiting an answer in the UI. + */ +@property (nonatomic) NSUInteger lifetime; + +/** + Indicate whether the negotiation is for a video call. + */ +- (BOOL)isVideoCall; + +@end diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m new file mode 100644 index 0000000000..1273bc95ff --- /dev/null +++ b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m @@ -0,0 +1,40 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXCallNegotiateEventContent.h" + +@implementation MXCallNegotiateEventContent + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXCallNegotiateEventContent *callNegotiateEventContent = [[MXCallNegotiateEventContent alloc] init]; + if (callNegotiateEventContent) + { + [callNegotiateEventContent parseJSON:JSONDictionary]; + MXJSONModelSetString(callNegotiateEventContent.callId, JSONDictionary[@"call_id"]); + MXJSONModelSetMXJSONModel(callNegotiateEventContent.offer, MXCallSessionDescription, JSONDictionary[@"description"]); + MXJSONModelSetUInteger(callNegotiateEventContent.lifetime, JSONDictionary[@"lifetime"]); + } + + return callNegotiateEventContent; +} + +- (BOOL)isVideoCall +{ + return (NSNotFound != [self.offer.sdp rangeOfString:@"m=video"].location); +} + +@end diff --git a/MatrixSDK/JSONModels/MXEvent.h b/MatrixSDK/JSONModels/MXEvent.h index 48d68fc8a5..5320d5b7f1 100644 --- a/MatrixSDK/JSONModels/MXEvent.h +++ b/MatrixSDK/JSONModels/MXEvent.h @@ -71,6 +71,7 @@ typedef NS_ENUM(NSInteger, MXEventType) MXEventTypeCallSelectAnswer, MXEventTypeCallHangup, MXEventTypeCallReject, + MXEventTypeCallNegotiate, MXEventTypeSticker, MXEventTypeRoomTombStone, MXEventTypeKeyVerificationRequest, @@ -131,6 +132,7 @@ FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallAnswer; FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallSelectAnswer; FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallHangup; FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallReject; +FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallNegotiate; FOUNDATION_EXPORT NSString *const kMXEventTypeStringSticker; FOUNDATION_EXPORT NSString *const kMXEventTypeStringRoomTombStone; diff --git a/MatrixSDK/JSONModels/MXEvent.m b/MatrixSDK/JSONModels/MXEvent.m index dbf02c2826..b1e2230dda 100644 --- a/MatrixSDK/JSONModels/MXEvent.m +++ b/MatrixSDK/JSONModels/MXEvent.m @@ -62,6 +62,7 @@ NSString *const kMXEventTypeStringCallSelectAnswer = @"m.call.select_answer"; NSString *const kMXEventTypeStringCallHangup = @"m.call.hangup"; NSString *const kMXEventTypeStringCallReject = @"m.call.reject"; +NSString *const kMXEventTypeStringCallNegotiate = @"m.call.negotiate"; NSString *const kMXEventTypeStringSticker = @"m.sticker"; NSString *const kMXEventTypeStringRoomTombStone = @"m.room.tombstone"; NSString *const kMXEventTypeStringKeyVerificationRequest= @"m.key.verification.request"; diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 6d07d9b1aa..9e971d04e5 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -29,6 +29,7 @@ #import "MXCallSelectAnswerEventContent.h" #import "MXCallCandidatesEventContent.h" #import "MXCallRejectEventContent.h" +#import "MXCallNegotiateEventContent.h" #pragma mark - Constants definitions NSString *const kMXCallStateDidChange = @"kMXCallStateDidChange"; @@ -83,7 +84,7 @@ @interface MXCall () /** Selected answer for this call. Only exists for outgoing calls and after an answer (an accept or reject) received. */ - MXCallEventContent *selectedAnswer; + MXEvent *selectedAnswer; } @end @@ -294,7 +295,7 @@ - (void)handleCallEvent:(MXEvent *)event } // mark this as the selected one - selectedAnswer = content; + selectedAnswer = event; void(^continueBlock)(void) = ^{ // Let's the stack finalise the connection @@ -406,7 +407,7 @@ - (void)handleCallEvent:(MXEvent *)event if (_state != MXCallStateEnded) { MXCallRejectEventContent *content = [MXCallRejectEventContent modelFromJSON:event.content]; - selectedAnswer = content; + selectedAnswer = event; NSDictionary *selectAnswerContent = @{ @"call_id": self.callId, @@ -433,6 +434,23 @@ - (void)handleCallEvent:(MXEvent *)event break; } + case MXEventTypeCallNegotiate: + { + if (!isMyEvent) + { + if (selectedAnswer && [selectedAnswer.sender isEqualToString:event.sender]) + { + MXCallEventContent *selectedAnswerContent = [MXCallEventContent modelFromJSON:selectedAnswer.content]; + MXCallNegotiateEventContent *content = [MXCallNegotiateEventContent modelFromJSON:event.content]; + + if ([selectedAnswerContent.partyId isEqualToString:content.partyId]) + { + // user-id and party-id matches + } + } + } + break; + } default: break; } diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 433620d25e..e14d89e838 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -31,6 +31,7 @@ #import "MXCallHangupEventContent.h" #import "MXCallCandidatesEventContent.h" #import "MXCallRejectEventContent.h" +#import "MXCallNegotiateEventContent.h" #pragma mark - Constants definitions NSString *const kMXCallManagerNewCall = @"kMXCallManagerNewCall"; @@ -83,7 +84,8 @@ - (instancetype)initWithMatrixSession:(MXSession *)mxSession andCallStack:(id Date: Mon, 16 Nov 2020 15:58:46 +0300 Subject: [PATCH 10/89] Add negotiate event type --- MatrixSDK/MXSession.m | 1 + MatrixSDK/Utils/MXTools.m | 1 + 2 files changed, 2 insertions(+) diff --git a/MatrixSDK/MXSession.m b/MatrixSDK/MXSession.m index c33e6743f7..c92e4f7735 100644 --- a/MatrixSDK/MXSession.m +++ b/MatrixSDK/MXSession.m @@ -234,6 +234,7 @@ - (id)initWithMatrixRestClient:(MXRestClient*)mxRestClient kMXEventTypeStringCallSelectAnswer, kMXEventTypeStringCallHangup, kMXEventTypeStringCallReject, + kMXEventTypeStringCallNegotiate, kMXEventTypeStringSticker ]; diff --git a/MatrixSDK/Utils/MXTools.m b/MatrixSDK/Utils/MXTools.m index 34bd57abc4..728a066840 100644 --- a/MatrixSDK/Utils/MXTools.m +++ b/MatrixSDK/Utils/MXTools.m @@ -105,6 +105,7 @@ + (void)initialize kMXEventTypeStringCallSelectAnswer, kMXEventTypeStringCallHangup, kMXEventTypeStringCallReject, + kMXEventTypeStringCallNegotiate, kMXEventTypeStringSticker, kMXEventTypeStringRoomTombStone, kMXEventTypeStringKeyVerificationRequest, From 1c6add0ddfebbc21ba21229f117934437c3072a6 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 20 Nov 2020 14:15:38 +0300 Subject: [PATCH 11/89] Introduce MXCallSessionDescriptionType --- .../Call/Events/MXCallNegotiateEventContent.h | 2 +- .../Call/Events/MXCallNegotiateEventContent.m | 4 +- .../Call/MXCallSessionDescription.h | 26 ++++++++++- .../Call/MXCallSessionDescription.m | 18 +++++++- MatrixSDK/Utils/MXTools.h | 4 ++ MatrixSDK/Utils/MXTools.m | 46 +++++++++++++++++++ 6 files changed, 94 insertions(+), 6 deletions(-) diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h index bbeda752a0..7c40802db4 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h @@ -31,7 +31,7 @@ /** The session description. */ -@property (nonatomic) MXCallSessionDescription *offer; +@property (nonatomic) MXCallSessionDescription *sessionDescription; /** The time in milliseconds that the invite is valid for. diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m index 1273bc95ff..5017554305 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m @@ -25,7 +25,7 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary { [callNegotiateEventContent parseJSON:JSONDictionary]; MXJSONModelSetString(callNegotiateEventContent.callId, JSONDictionary[@"call_id"]); - MXJSONModelSetMXJSONModel(callNegotiateEventContent.offer, MXCallSessionDescription, JSONDictionary[@"description"]); + MXJSONModelSetMXJSONModel(callNegotiateEventContent.sessionDescription, MXCallSessionDescription, JSONDictionary[@"description"]); MXJSONModelSetUInteger(callNegotiateEventContent.lifetime, JSONDictionary[@"lifetime"]); } @@ -34,7 +34,7 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary - (BOOL)isVideoCall { - return (NSNotFound != [self.offer.sdp rangeOfString:@"m=video"].location); + return (NSNotFound != [self.sessionDescription.sdp rangeOfString:@"m=video"].location); } @end diff --git a/MatrixSDK/JSONModels/Call/MXCallSessionDescription.h b/MatrixSDK/JSONModels/Call/MXCallSessionDescription.h index bd2b4d092d..d3ab1acb41 100644 --- a/MatrixSDK/JSONModels/Call/MXCallSessionDescription.h +++ b/MatrixSDK/JSONModels/Call/MXCallSessionDescription.h @@ -17,19 +17,41 @@ #import #import "MXJSONModel.h" +typedef NSString * MXCallSessionDescriptionTypeString; +FOUNDATION_EXPORT NSString *const kMXCallSessionDescriptionTypeOffer; +FOUNDATION_EXPORT NSString *const kMXCallSessionDescriptionTypePrAnswer; +FOUNDATION_EXPORT NSString *const kMXCallSessionDescriptionTypeAnswer; +FOUNDATION_EXPORT NSString *const kMXCallSessionDescriptionTypeRollback; + +/** + MXCallSessionDescription types + */ +typedef enum : NSUInteger +{ + MXCallSessionDescriptionTypeOffer, + MXCallSessionDescriptionTypePrAnswer, + MXCallSessionDescriptionTypeAnswer, + MXCallSessionDescriptionTypeRollback +} MXCallSessionDescriptionType NS_REFINED_FOR_SWIFT; + /** `MXCallOffer` represents a call session description. */ @interface MXCallSessionDescription : MXJSONModel /** - The type of session description. Can be 'offer' or 'answer'. + The type of session description (as string). */ -@property (nonatomic) NSString *type; +@property (nonatomic) MXCallSessionDescriptionTypeString typeString; /** The SDP text of the session description. */ @property (nonatomic) NSString *sdp; +/** + The mapped enum type of session description. + */ +@property (nonatomic) MXCallSessionDescriptionType type; + @end diff --git a/MatrixSDK/JSONModels/Call/MXCallSessionDescription.m b/MatrixSDK/JSONModels/Call/MXCallSessionDescription.m index ad00558e94..e89b7dadc0 100644 --- a/MatrixSDK/JSONModels/Call/MXCallSessionDescription.m +++ b/MatrixSDK/JSONModels/Call/MXCallSessionDescription.m @@ -15,6 +15,12 @@ // #import "MXCallSessionDescription.h" +#import "MXTools.h" + +NSString *const kMXCallSessionDescriptionTypeOffer = @"offer"; +NSString *const kMXCallSessionDescriptionTypePrAnswer = @"pranswer"; +NSString *const kMXCallSessionDescriptionTypeAnswer = @"answer"; +NSString *const kMXCallSessionDescriptionTypeRollback = @"rollback"; @implementation MXCallSessionDescription @@ -23,11 +29,21 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary MXCallSessionDescription *callSessionDescription = [[MXCallSessionDescription alloc] init]; if (callSessionDescription) { - MXJSONModelSetString(callSessionDescription.type, JSONDictionary[@"type"]); + MXJSONModelSetString(callSessionDescription.typeString, JSONDictionary[@"type"]); MXJSONModelSetString(callSessionDescription.sdp, JSONDictionary[@"sdp"]); } return callSessionDescription; } +- (MXCallSessionDescriptionType)type +{ + return [MXTools callSessionDescriptionType:self.typeString]; +} + +- (void)setType:(MXCallSessionDescriptionType)type +{ + self.typeString = [MXTools callSessionDescriptionTypeString:type]; +} + @end diff --git a/MatrixSDK/Utils/MXTools.h b/MatrixSDK/Utils/MXTools.h index f5c2dc2694..013f4dddc5 100644 --- a/MatrixSDK/Utils/MXTools.h +++ b/MatrixSDK/Utils/MXTools.h @@ -27,6 +27,7 @@ #import "MXJSONModels.h" #import "MXEnumConstants.h" #import "MXCallHangupEventContent.h" +#import "MXCallSessionDescription.h" @interface MXTools : NSObject @@ -42,6 +43,9 @@ + (MXCallHangupReason)callHangupReason:(MXCallHangupReasonString)reasonString; + (MXCallHangupReasonString)callHangupReasonString:(MXCallHangupReason)reason; ++ (MXCallSessionDescriptionType)callSessionDescriptionType:(MXCallSessionDescriptionTypeString)typeString; ++ (MXCallSessionDescriptionTypeString)callSessionDescriptionTypeString:(MXCallSessionDescriptionType)type; + /** Generate a random secret key. diff --git a/MatrixSDK/Utils/MXTools.m b/MatrixSDK/Utils/MXTools.m index 728a066840..b65a1e8d17 100644 --- a/MatrixSDK/Utils/MXTools.m +++ b/MatrixSDK/Utils/MXTools.m @@ -340,6 +340,52 @@ + (MXCallHangupReasonString)callHangupReasonString:(MXCallHangupReason)reason return string; } ++ (MXCallSessionDescriptionType)callSessionDescriptionType:(MXCallSessionDescriptionTypeString)typeString +{ + MXCallSessionDescriptionType type = MXCallSessionDescriptionTypeOffer; + + if ([typeString isEqualToString:kMXCallSessionDescriptionTypeOffer]) + { + type = MXCallSessionDescriptionTypeOffer; + } + else if ([typeString isEqualToString:kMXCallSessionDescriptionTypePrAnswer]) + { + type = MXCallSessionDescriptionTypePrAnswer; + } + else if ([typeString isEqualToString:kMXCallSessionDescriptionTypeAnswer]) + { + type = MXCallSessionDescriptionTypeAnswer; + } + else if ([typeString isEqualToString:kMXCallSessionDescriptionTypeRollback]) + { + type = MXCallSessionDescriptionTypeRollback; + } + + return type; +} + ++ (MXCallSessionDescriptionTypeString)callSessionDescriptionTypeString:(MXCallSessionDescriptionType)type +{ + MXCallSessionDescriptionTypeString string; + + switch (type) { + case MXCallSessionDescriptionTypeOffer: + string = kMXCallSessionDescriptionTypeOffer; + break; + case MXCallSessionDescriptionTypePrAnswer: + string = kMXCallSessionDescriptionTypePrAnswer; + break; + case MXCallSessionDescriptionTypeAnswer: + string = kMXCallSessionDescriptionTypeAnswer; + break; + case MXCallSessionDescriptionTypeRollback: + string = kMXCallSessionDescriptionTypeRollback; + break; + } + + return string; +} + + (NSString *)generateSecret { return [[NSProcessInfo processInfo] globallyUniqueString]; From f605ab1a8b4b6fc59fdea6b6eb5e78d0bce271f3 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 20 Nov 2020 14:21:42 +0300 Subject: [PATCH 12/89] Implement hold & resume --- MatrixSDK/VoIP/CallStack/MXCallStackCall.h | 21 +- MatrixSDK/VoIP/MXCall.h | 6 + MatrixSDK/VoIP/MXCall.m | 700 +++++++++++------- MatrixSDK/VoIP/MXCallManager.h | 6 + MatrixSDK/VoIP/MXCallManager.m | 1 + .../VoIP/Jingle/MXJingleCallStackCall.m | 187 ++++- 6 files changed, 657 insertions(+), 264 deletions(-) diff --git a/MatrixSDK/VoIP/CallStack/MXCallStackCall.h b/MatrixSDK/VoIP/CallStack/MXCallStackCall.h index 65f38659d0..cecc068347 100644 --- a/MatrixSDK/VoIP/CallStack/MXCallStackCall.h +++ b/MatrixSDK/VoIP/CallStack/MXCallStackCall.h @@ -46,6 +46,19 @@ NS_ASSUME_NONNULL_BEGIN success:(void (^)(void))success failure:(void (^)(NSError *error))failure; +/** + Hold/resume the call. Creates an offer. + + The created sdp will be sent to the Matrix room in a m.call.negotiate event. + + @param success A block object called when the operation succeeds. It provides a description + of the offer. + @param failure A block object called when the operation fails. + */ +- (void)hold:(BOOL)hold + success:(void (^)(NSString *sdp))success + failure:(void (^)(NSError *))failure; + /** Terminate the call. */ @@ -101,7 +114,6 @@ NS_ASSUME_NONNULL_BEGIN - (void)createAnswer:(void (^)(NSString *sdpAnswer))success failure:(void (^)(NSError *error))failure; - #pragma mark - Outgoing call /** Create an offer. @@ -218,6 +230,13 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)callStackCallDidConnect:(id)callStackCall; +/** + Tells the delegate that connection was holded + + @param callStackCall the corresponding instance. + */ +- (void)callStackCallDidHold:(id)callStackCall; + @end NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/VoIP/MXCall.h b/MatrixSDK/VoIP/MXCall.h index 7e985133db..787528ec70 100644 --- a/MatrixSDK/VoIP/MXCall.h +++ b/MatrixSDK/VoIP/MXCall.h @@ -47,6 +47,8 @@ typedef NS_ENUM(NSUInteger, MXCallState) MXCallStateConnecting, MXCallStateConnected, + MXCallStateHolded, + MXCallStateRemoteHolded, MXCallStateEnded, MXCallStateInviteExpired, @@ -120,6 +122,10 @@ extern NSString *const kMXCallStateDidChange; */ - (void)answer; +@property (nonatomic, readonly) BOOL supportsHolding; + +- (void)hold:(BOOL)hold; + /** Hang up a call in progress or reject an incoming call. */ diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 9e971d04e5..13d1e886fa 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -47,7 +47,7 @@ @interface MXCall () id callStackCall; /** - The invite received by the peer + The invite sent to/received by the peer */ MXCallInviteEventContent *callInviteEventContent; @@ -201,262 +201,34 @@ - (void)calleeId:(void (^)(NSString * _Nonnull))onComplete - (void)handleCallEvent:(MXEvent *)event { - BOOL isMyEvent = [self isMyEvent:event]; - switch (event.eventType) { case MXEventTypeCallInvite: - { - callInviteEventContent = [MXCallInviteEventContent modelFromJSON:event.content]; - - if (!isMyEvent) - { - // Incoming call - - if (_state >= MXCallStateRinging) - { - // already ringing, do nothing - return; - } - - _callId = callInviteEventContent.callId; - _callerId = event.sender; - calleeId = callManager.mxSession.myUserId; - _isIncoming = YES; - - // Store if it is voice or video call - _isVideoCall = callInviteEventContent.isVideoCall; - - // Set up the default audio route - callStackCall.audioToSpeaker = _isVideoCall; - - [self setState:MXCallStateWaitLocalMedia reason:nil]; - - MXWeakify(self); - [callStackCall startCapturingMediaWithVideo:self.isVideoCall success:^{ - MXStrongifyAndReturnIfNil(self); - - MXWeakify(self); - [self->callStackCall handleOffer:self->callInviteEventContent.offer.sdp - success:^{ - MXStrongifyAndReturnIfNil(self); - - // Check whether the call has not been ended. - if (self.state != MXCallStateEnded) - { - [self setState:MXCallStateRinging reason:event]; - } - } - failure:^(NSError * _Nonnull error) { - NSLog(@"[MXCall] handleOffer: ERROR: Couldn't handle offer. Error: %@", error); - [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; - }]; - } failure:^(NSError *error) { - NSLog(@"[MXCall] startCapturingMediaWithVideo: ERROR: Couldn't start capturing. Error: %@", error); - [self didEncounterError:error reason:MXCallHangupReasonUserMediaFailed]; - }]; - } - else - { - // Outgoing call. This is the invite event we sent - } - - // Start expiration timer - inviteExpirationTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:callInviteEventContent.lifetime / 1000] - interval:0 - target:self - selector:@selector(expireCallInvite) - userInfo:nil - repeats:NO]; - [[NSRunLoop mainRunLoop] addTimer:inviteExpirationTimer forMode:NSDefaultRunLoopMode]; - + [self handleCallInvite:event]; break; - } - case MXEventTypeCallAnswer: - { - // Listen to answer event only for call we are making, not receiving - if (!_isIncoming) - { - if (selectedAnswer) - { - // there is already a selected answer, ignore this one - return; - } - - // MXCall receives this event only when it placed a call - MXCallAnswerEventContent *content = [MXCallAnswerEventContent modelFromJSON:event.content]; - - // The peer accepted our outgoing call - if (inviteExpirationTimer) - { - [inviteExpirationTimer invalidate]; - inviteExpirationTimer = nil; - } - - // mark this as the selected one - selectedAnswer = event; - - void(^continueBlock)(void) = ^{ - // Let's the stack finalise the connection - [self setState:MXCallStateConnecting reason:event]; - [self->callStackCall handleAnswer:content.answer.sdp - success:^{} - failure:^(NSError *error) { - NSLog(@"[MXCall] handleCallEvent: ERROR: Cannot send handle answer. Error: %@\nEvent: %@", error, event); - self->selectedAnswer = nil; - [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; - }]; - }; - - if ([content.version isEqualToString:kMXCallVersion]) - { - NSDictionary *selectAnswerContent = @{ - @"call_id": self.callId, - @"version": kMXCallVersion, - @"party_id": self.partyId, - @"selected_party_id": content.partyId - }; - - [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallSelectAnswer - content:selectAnswerContent - localEcho:nil - success:^(NSString *eventId) { - - continueBlock(); - - } failure:^(NSError *error) { - NSLog(@"[MXCall] callWithVideo: ERROR: Cannot send m.call.select_answer event. Error: %@\n", error); - self->selectedAnswer = nil; - [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; - }]; - } - else - { - continueBlock(); - } - } - else if (_state == MXCallStateRinging) - { - // Else this event means that the call has been answered by the user from - // another device - [self onCallAnsweredElsewhere]; - } + [self handleCallAnswer:event]; break; - } - case MXEventTypeCallSelectAnswer: - { - if (!isMyEvent) - { - if (_isIncoming) - { - MXCallSelectAnswerEventContent *content = [MXCallSelectAnswerEventContent modelFromJSON:event.content]; - if (![content.selectedPartyId isEqualToString:self.partyId]) - { - // Else this event means that the call has been answered (accepted/rejected) by another user/device - [self onCallAnsweredElsewhere]; - } - } - } + [self handleCallSelectAnswer:event]; break; - } - case MXEventTypeCallHangup: - { - if (_state != MXCallStateEnded) - { - [self terminateWithReason:event]; - } + [self handleCallHangup:event]; break; - } - case MXEventTypeCallCandidates: - { - if (!isMyEvent) - { - MXCallCandidatesEventContent *content = [MXCallCandidatesEventContent modelFromJSON:event.content]; - - NSLog(@"[MXCall] handleCallCandidates: %@", content.candidates); - for (MXCallCandidate *canditate in content.candidates) - { - [callStackCall handleRemoteCandidate:canditate.JSONDictionary]; - } - } + [self handleCallCandidates:event]; break; - } - case MXEventTypeCallReject: - { - // Listen to answer event only for call we are making, not receiving - if (!_isIncoming) - { - if (selectedAnswer) - { - // there is already a selected answer, ignore this one - return; - } - - // The peer rejected our outgoing call - if (inviteExpirationTimer) - { - [inviteExpirationTimer invalidate]; - inviteExpirationTimer = nil; - } - - if (_state != MXCallStateEnded) - { - MXCallRejectEventContent *content = [MXCallRejectEventContent modelFromJSON:event.content]; - selectedAnswer = event; - - NSDictionary *selectAnswerContent = @{ - @"call_id": self.callId, - @"version": kMXCallVersion, - @"party_id": self.partyId, - @"selected_party_id": content.partyId - }; - - [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallSelectAnswer - content:selectAnswerContent - localEcho:nil - success:^(NSString *eventId) { - - [self terminateWithReason:event]; - - } failure:^(NSError *error) { - NSLog(@"[MXCall] callWithVideo: ERROR: Cannot send m.call.select_answer event. Error: %@\n", error); - self->selectedAnswer = nil; - [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; - }]; - } - } - + [self handleCallReject:event]; break; - } - case MXEventTypeCallNegotiate: - { - if (!isMyEvent) - { - if (selectedAnswer && [selectedAnswer.sender isEqualToString:event.sender]) - { - MXCallEventContent *selectedAnswerContent = [MXCallEventContent modelFromJSON:selectedAnswer.content]; - MXCallNegotiateEventContent *content = [MXCallNegotiateEventContent modelFromJSON:event.content]; - - if ([selectedAnswerContent.partyId isEqualToString:content.partyId]) - { - // user-id and party-id matches - } - } - } + [self handleCallNegotiate:event]; break; - } default: break; } } - #pragma mark - Controls - (void)callWithVideo:(BOOL)video { @@ -487,7 +259,7 @@ - (void)callWithVideo:(BOOL)video NSMutableDictionary *content = [@{ @"call_id": self.callId, @"offer": @{ - @"type": @"offer", + @"type": kMXCallSessionDescriptionTypeOffer, @"sdp": sdp }, @"version": kMXCallVersion, @@ -503,6 +275,7 @@ - (void)callWithVideo:(BOOL)video [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallInvite content:content localEcho:nil success:^(NSString *eventId) { + self->callInviteEventContent = [MXCallInviteEventContent modelFromJSON:content]; [self setState:MXCallStateInviteSent reason:nil]; } failure:^(NSError *error) { @@ -557,23 +330,30 @@ - (void)answer NSDictionary *content = @{ @"call_id": self.callId, @"answer": @{ - @"type": @"answer", + @"type": kMXCallSessionDescriptionTypeAnswer, @"sdp": sdpAnswer }, @"version": kMXCallVersion, @"party_id": self.partyId }; - [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallAnswer content:content localEcho:nil success:nil failure:^(NSError *error) { + [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallAnswer content:content localEcho:nil success:^(NSString *eventId){ + // assume for now, this is the selected answer + selectedAnswer = [MXEvent modelFromJSON:@{ + @"event_id": eventId, + @"sender": self.callSignalingRoom.mxSession.myUserId, + @"room_id": self.callSignalingRoom.roomId, + @"type": kMXEventTypeStringCallAnswer, + @"content": content + }]; + } failure:^(NSError *error) { NSLog(@"[MXCall] answer: ERROR: Cannot send m.call.answer event."); [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; } failure:^(NSError *error) { - NSLog(@"[MXCall] answer: ERROR: Cannot create offer. Error: %@", error); + NSLog(@"[MXCall] answer: ERROR: Cannot create answer. Error: %@", error); [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; }]; - - self->callInviteEventContent = nil; }; // If the room is encrypted, we need to check that encryption is set up @@ -595,6 +375,86 @@ - (void)answer } } +- (BOOL)supportsHolding +{ + if (callInviteEventContent && selectedAnswer && [callInviteEventContent.version isEqualToString:kMXCallVersion]) + { + MXCallAnswerEventContent *content = [MXCallAnswerEventContent modelFromJSON:selectedAnswer.content]; + return [content.version isEqualToString:kMXCallVersion] || [content.version isEqualToString:@"0"]; + } + return NO; +} + +- (void)hold:(BOOL)hold +{ + if (_state < MXCallStateConnected) + { + // call not connected yet, cannot be holded/unholded + return; + } + + if (hold) + { + if (_state == MXCallStateHolded || _state == MXCallStateRemoteHolded) + { + // already holded + return; + } + } + else + { + if (_state == MXCallStateRemoteHolded) + { + // remotely holded calls cannot be unholded + return; + } + if (_state == MXCallStateConnected) + { + // already connected + return; + } + } + + MXWeakify(self); + [callStackCall hold:hold success:^(NSString * _Nonnull sdp) { + MXStrongifyAndReturnIfNil(self); + + NSLog(@"[MXCall] %@ offer created: %@", (hold ? @"Hold" : @"Resume"), sdp); + + // The call hold offer can sent to the HS + NSMutableDictionary *content = [@{ + @"call_id": self.callId, + @"description": @{ + @"type": kMXCallSessionDescriptionTypeOffer, + @"sdp": sdp + }, + @"version": kMXCallVersion, + @"lifetime": @(self->callManager.negotiateLifetime), + @"party_id": self.partyId + } mutableCopy]; + + [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallNegotiate content:content localEcho:nil success:^(NSString *eventId) { + + if (hold) + { + [self setState:MXCallStateHolded reason:nil]; + } + else + { + [self setState:MXCallStateConnected reason:nil]; + } + + } failure:^(NSError *error) { + NSLog(@"[MXCall] callWithVideo: ERROR: Cannot send m.call.negotiate event."); + [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; + }]; + + } failure:^(NSError * _Nonnull error) { + NSLog(@"[MXCall] callWithVideo: ERROR: Cannot create %@ offer. Error: %@", (hold ? @"Hold" : @"Resume"), error); + [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; + }]; +} + - (void)hangup { NSLog(@"[MXCall] hangup"); @@ -663,11 +523,14 @@ - (void)setState:(MXCallState)state reason:(MXEvent *)event // Manage call duration if (MXCallStateConnected == state) { - // Set the start point - callConnectedDate = [NSDate date]; - - // Mark call as established - _established = YES; + if (_state != MXCallStateHolded && _state != MXCallStateRemoteHolded) + { + // Set the start point + callConnectedDate = [NSDate date]; + + // Mark call as established + _established = YES; + } } else if (MXCallStateEnded == state) { @@ -845,6 +708,14 @@ - (void)sendLocalIceCandidates } } +- (void)callStackCallDidHold:(id)callStackCall +{ + if (self.state == MXCallStateConnected) + { + [self setState:MXCallStateRemoteHolded reason:nil]; + } +} + - (void)callStackCall:(id)callStackCall onError:(NSError *)error { NSLog(@"[MXCall] callStackCall didEncounterError: %@", error); @@ -861,15 +732,348 @@ - (void)callStackCall:(id)callStackCall onError:(NSError *)erro - (void)callStackCallDidConnect:(id)callStackCall { - if (self.state == MXCallStateConnecting) + if (self.state == MXCallStateConnecting || self.state == MXCallStateRemoteHolded) { [self setState:MXCallStateConnected reason:nil]; } } +#pragma mark - Event Handlers + +- (void)handleCallInvite:(MXEvent *)event +{ + callInviteEventContent = [MXCallInviteEventContent modelFromJSON:event.content]; + + if ([self isMyEvent:event]) + { + return; + } + + // Incoming call + + if (_state >= MXCallStateRinging) + { + // already ringing, do nothing + return; + } + + _callId = callInviteEventContent.callId; + _callerId = event.sender; + calleeId = callManager.mxSession.myUserId; + _isIncoming = YES; + + // Store if it is voice or video call + _isVideoCall = callInviteEventContent.isVideoCall; + + // Set up the default audio route + callStackCall.audioToSpeaker = _isVideoCall; + + [self setState:MXCallStateWaitLocalMedia reason:nil]; + + MXWeakify(self); + [callStackCall startCapturingMediaWithVideo:self.isVideoCall success:^{ + MXStrongifyAndReturnIfNil(self); + + MXWeakify(self); + [self->callStackCall handleOffer:self->callInviteEventContent.offer.sdp + success:^{ + MXStrongifyAndReturnIfNil(self); + + // Check whether the call has not been ended. + if (self.state != MXCallStateEnded) + { + [self setState:MXCallStateRinging reason:event]; + } + } + failure:^(NSError * _Nonnull error) { + NSLog(@"[MXCall] handleOffer: ERROR: Couldn't handle offer. Error: %@", error); + [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; + }]; + } failure:^(NSError *error) { + NSLog(@"[MXCall] startCapturingMediaWithVideo: ERROR: Couldn't start capturing. Error: %@", error); + [self didEncounterError:error reason:MXCallHangupReasonUserMediaFailed]; + }]; + + // Start expiration timer + inviteExpirationTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:callInviteEventContent.lifetime / 1000] + interval:0 + target:self + selector:@selector(expireCallInvite) + userInfo:nil + repeats:NO]; + [[NSRunLoop mainRunLoop] addTimer:inviteExpirationTimer forMode:NSDefaultRunLoopMode]; +} + +- (void)handleCallAnswer:(MXEvent *)event +{ + if ([self isMyEvent:event]) + { + return; + } + + // Listen to answer event only for call we are making, not receiving + if (!_isIncoming) + { + if (selectedAnswer) + { + // there is already a selected answer, ignore this one + return; + } + + // MXCall receives this event only when it placed a call + MXCallAnswerEventContent *content = [MXCallAnswerEventContent modelFromJSON:event.content]; + + // The peer accepted our outgoing call + if (inviteExpirationTimer) + { + [inviteExpirationTimer invalidate]; + inviteExpirationTimer = nil; + } + + // mark this as the selected one + selectedAnswer = event; + + void(^continueBlock)(void) = ^{ + // Let's the stack finalise the connection + [self setState:MXCallStateConnecting reason:event]; + [self->callStackCall handleAnswer:content.answer.sdp + success:^{} + failure:^(NSError *error) { + NSLog(@"[MXCall] handleCallEvent: ERROR: Cannot send handle answer. Error: %@\nEvent: %@", error, event); + self->selectedAnswer = nil; + [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; + }]; + }; + + if ([content.version isEqualToString:kMXCallVersion]) + { + NSDictionary *selectAnswerContent = @{ + @"call_id": self.callId, + @"version": kMXCallVersion, + @"party_id": self.partyId, + @"selected_party_id": content.partyId + }; + + [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallSelectAnswer + content:selectAnswerContent + localEcho:nil + success:^(NSString *eventId) { + + continueBlock(); + + } failure:^(NSError *error) { + NSLog(@"[MXCall] callWithVideo: ERROR: Cannot send m.call.select_answer event. Error: %@\n", error); + self->selectedAnswer = nil; + [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; + }]; + } + else + { + continueBlock(); + } + } + else if (_state == MXCallStateRinging) + { + // Else this event means that the call has been answered by the user from + // another device + [self onCallAnsweredElsewhere]; + } +} + +- (void)handleCallSelectAnswer:(MXEvent *)event +{ + if ([self isMyEvent:event]) + { + return; + } + + if (_isIncoming) + { + MXCallSelectAnswerEventContent *content = [MXCallSelectAnswerEventContent modelFromJSON:event.content]; + if (selectedAnswer) + { + // check assumed selectedAnswer is really the selected one + MXCallAnswerEventContent *selectedAnswerContent = [MXCallAnswerEventContent modelFromJSON:selectedAnswer.content]; + if (![selectedAnswerContent.partyId isEqualToString:content.selectedPartyId]) + { + // a different answer is selected + selectedAnswer = nil; + } + } + if (![content.selectedPartyId isEqualToString:self.partyId]) + { + // Else this event means that the call has been answered (accepted/rejected) by another user/device + [self onCallAnsweredElsewhere]; + } + } +} + +- (void)handleCallHangup:(MXEvent *)event +{ + if (_state != MXCallStateEnded) + { + [self terminateWithReason:event]; + } +} + +- (void)handleCallCandidates:(MXEvent *)event +{ + if ([self isMyEvent:event]) + { + return; + } + + MXCallCandidatesEventContent *content = [MXCallCandidatesEventContent modelFromJSON:event.content]; + + NSLog(@"[MXCall] handleCallCandidates: %@", content.candidates); + for (MXCallCandidate *canditate in content.candidates) + { + [callStackCall handleRemoteCandidate:canditate.JSONDictionary]; + } +} + +- (void)handleCallReject:(MXEvent *)event +{ + if ([self isMyEvent:event]) + { + return; + } + + // Listen to answer event only for call we are making, not receiving + if (!_isIncoming) + { + if (selectedAnswer) + { + // there is already a selected answer, ignore this one + return; + } + + // The peer rejected our outgoing call + if (inviteExpirationTimer) + { + [inviteExpirationTimer invalidate]; + inviteExpirationTimer = nil; + } + + if (_state != MXCallStateEnded) + { + MXCallRejectEventContent *content = [MXCallRejectEventContent modelFromJSON:event.content]; + selectedAnswer = event; + + NSDictionary *selectAnswerContent = @{ + @"call_id": self.callId, + @"version": kMXCallVersion, + @"party_id": self.partyId, + @"selected_party_id": content.partyId + }; + + [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallSelectAnswer + content:selectAnswerContent + localEcho:nil + success:^(NSString *eventId) { + + [self terminateWithReason:event]; + + } failure:^(NSError *error) { + NSLog(@"[MXCall] callWithVideo: ERROR: Cannot send m.call.select_answer event. Error: %@\n", error); + self->selectedAnswer = nil; + [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; + }]; + } + } +} + +- (void)handleCallNegotiate:(MXEvent *)event +{ + if ([self isMyEvent:event]) + { + return; + } + + if (![self canHandleNegotiationEvent:event]) + { + return; + } + + MXCallNegotiateEventContent *content = [MXCallNegotiateEventContent modelFromJSON:event.content]; + + if (content.sessionDescription.type == MXCallSessionDescriptionTypeOffer) + { + // Store if it is voice or video call + _isVideoCall = content.isVideoCall; + + // Set up the default audio route + callStackCall.audioToSpeaker = _isVideoCall; + + MXWeakify(self); + [self->callStackCall handleOffer:content.sessionDescription.sdp + success:^{ + MXStrongifyAndReturnIfNil(self); + + // TODO: Get offer type from handleOffer and decide auto-acppet it or not + // auto-accept negotiations for now + [self->callStackCall createAnswer:^(NSString * _Nonnull sdpAnswer) { + MXStrongifyAndReturnIfNil(self); + + NSLog(@"[MXCall] answer negotiation - Created SDP:\n%@", sdpAnswer); + + NSDictionary *content = @{ + @"call_id": self.callId, + @"description": @{ + @"type": kMXCallSessionDescriptionTypeAnswer, + @"sdp": sdpAnswer + }, + @"version": kMXCallVersion, + @"party_id": self.partyId, + @"lifetime": @(self->callManager.negotiateLifetime) + }; + [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallNegotiate content:content localEcho:nil success:nil failure:^(NSError *error) { + NSLog(@"[MXCall] negotiate answer: ERROR: Cannot send m.call.negotiate event."); + [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; + }]; + } failure:^(NSError * _Nonnull error) { + NSLog(@"[MXCall] negotiate answer: ERROR: Cannot create negotiate answer. Error: %@", error); + [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; + }]; + } + failure:^(NSError * _Nonnull error) { + NSLog(@"[MXCall] handleOffer: ERROR: Couldn't handle negotiate offer. Error: %@", error); + [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; + }]; + } + else if (content.sessionDescription.type == MXCallSessionDescriptionTypeAnswer) + { + [self->callStackCall handleAnswer:content.sessionDescription.sdp + success:^{} + failure:^(NSError *error) { + NSLog(@"[MXCall] handleCallEvent: ERROR: Cannot send handle negotiate answer. Error: %@\nEvent: %@", error, event); + [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; + }]; + } +} #pragma mark - Private methods +- (BOOL)canHandleNegotiationEvent:(MXEvent *)event +{ + if (_isIncoming) + { + return YES; + } + + // outgoing call, check the event coming from the same user with the selected answer + if (selectedAnswer && [selectedAnswer.sender isEqualToString:event.sender]) + { + MXCallEventContent *selectedAnswerContent = [MXCallEventContent modelFromJSON:selectedAnswer.content]; + MXCallNegotiateEventContent *content = [MXCallNegotiateEventContent modelFromJSON:event.content]; + + // return if user-id and party-id matches + return [selectedAnswerContent.partyId isEqualToString:content.partyId]; + } + + return NO; +} + - (BOOL)isMyEvent:(MXEvent *)event { if ([event.sender isEqualToString:_callSignalingRoom.mxSession.myUserId]) diff --git a/MatrixSDK/VoIP/MXCallManager.h b/MatrixSDK/VoIP/MXCallManager.h index e80bc82539..a53b8ae5b6 100644 --- a/MatrixSDK/VoIP/MXCallManager.h +++ b/MatrixSDK/VoIP/MXCallManager.h @@ -138,6 +138,12 @@ extern NSString *const kMXCallManagerConferenceFinished; */ @property (nonatomic) NSUInteger inviteLifetime; +/** + The time in milliseconds that an incoming or outgoing call negotiate is valid for. + Default is 30s. + */ +@property (nonatomic) NSUInteger negotiateLifetime; + /** The list of TURN/STUN servers advertised by the user's homeserver. Can be nil. In this case, use `fallbackSTUNServer`. diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index e14d89e838..fee5e679c7 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -74,6 +74,7 @@ - (instancetype)initWithMatrixSession:(MXSession *)mxSession andCallStack:(idisVideoCall) + { + [self.captureController startCapture]; + } + mediaConstraints = self.mediaConstraints; + } + + MXWeakify(self); + [peerConnection offerForConstraints:mediaConstraints completionHandler:^(RTCSessionDescription * _Nullable sdp, NSError * _Nullable error) { + MXStrongifyAndReturnIfNil(self); + + if (!error) + { + // Report this sdp back to libjingle + [self->peerConnection setLocalDescription:sdp completionHandler:^(NSError * _Nullable error) { + NSLog(@"[MXJingleCallStackCall] hold: setLocalDescription: error: %@", error); + + // Return on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + + if (!error) + { + success(sdp.sdp); + } + else + { + failure(error); + } + + }); + }]; + } + else + { + // Return on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + + failure(error); + + }); + } + }]; +} + - (void)end { self.videoCapturer = nil; @@ -146,10 +209,11 @@ - (void)addTURNServerUris:(NSArray *)uris withUsername:(nullable NSS RTCMediaConstraints *constraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:nil optionalConstraints:@{ - @"RtpDataChannels": @"true" + @"RtpDataChannels": kRTCMediaConstraintsValueTrue }]; RTCConfiguration *configuration = [[RTCConfiguration alloc] init]; + configuration.sdpSemantics = RTCSdpSemanticsUnifiedPlan; if (ICEServer) { configuration.iceServers = @[ICEServer]; @@ -191,7 +255,7 @@ - (void)handleOffer:(NSString *)sdpOffer success:(void (^)(void))success failure RTCSessionDescription *sessionDescription = [[RTCSessionDescription alloc] initWithType:RTCSdpTypeOffer sdp:sdpOffer]; MXWeakify(self); [peerConnection setRemoteDescription:sessionDescription completionHandler:^(NSError * _Nullable error) { - NSLog(@"[MXJingleCallStackCall] setRemoteDescription: error: %@", error); + NSLog(@"[MXJingleCallStackCall] handleOffer: setRemoteDescription: error: %@", error); // Return on main thread dispatch_async(dispatch_get_main_queue(), ^{ @@ -217,7 +281,6 @@ - (void)handleOffer:(NSString *)sdpOffer success:(void (^)(void))success failure }]; } - - (void)createAnswer:(void (^)(NSString *))success failure:(void (^)(NSError *))failure { MXWeakify(self); @@ -226,15 +289,27 @@ - (void)createAnswer:(void (^)(NSString *))success failure:(void (^)(NSError *)) if (!error) { + MXWeakify(self); // Report this sdp back to libjingle [self->peerConnection setLocalDescription:sdp completionHandler:^(NSError * _Nullable error) { - + MXStrongifyAndReturnIfNil(self); + + NSLog(@"[MXJingleCallStackCall] createAnswer: setLocalDescription: error: %@", error); + // Return on main thread dispatch_async(dispatch_get_main_queue(), ^{ if (!error) { success(sdp.sdp); + if ([self isHoldOffer:sdp.sdp]) + { + [self.delegate callStackCallDidHold:self]; + } + else + { + [self.delegate callStackCallDidConnect:self]; + } } else { @@ -257,8 +332,8 @@ - (void)createAnswer:(void (^)(NSString *))success failure:(void (^)(NSError *)) }]; } - #pragma mark - Outgoing call + - (void)createOffer:(void (^)(NSString *sdp))success failure:(void (^)(NSError *))failure { MXWeakify(self); @@ -269,7 +344,8 @@ - (void)createOffer:(void (^)(NSString *sdp))success failure:(void (^)(NSError * { // Report this sdp back to libjingle [self->peerConnection setLocalDescription:sdp completionHandler:^(NSError * _Nullable error) { - + NSLog(@"[MXJingleCallStackCall] createOffer: setLocalDescription: error: %@", error); + // Return on main thread dispatch_async(dispatch_get_main_queue(), ^{ @@ -301,7 +377,8 @@ - (void)handleAnswer:(NSString *)sdp success:(void (^)(void))success failure:(vo { RTCSessionDescription *sessionDescription = [[RTCSessionDescription alloc] initWithType:RTCSdpTypeAnswer sdp:sdp]; [peerConnection setRemoteDescription:sessionDescription completionHandler:^(NSError * _Nullable error) { - + NSLog(@"[MXJingleCallStackCall] handleAnswer: setRemoteDescription: error: %@", error); + // Return on main thread dispatch_async(dispatch_get_main_queue(), ^{ @@ -324,7 +401,6 @@ - (void)handleAnswer:(NSString *)sdp success:(void (^)(void))success failure:(vo // Triggered when the SignalingState changed. - (void)peerConnection:(RTCPeerConnection *)peerConnection didChangeSignalingState:(RTCSignalingState)stateChanged - { NSLog(@"[MXJingleCallStackCall] didChangeSignalingState: %tu", stateChanged); } @@ -334,7 +410,7 @@ - (void)peerConnection:(RTCPeerConnection *)peerConnection didAddStream:(RTCMediaStream *)stream { NSLog(@"[MXJingleCallStackCall] didAddStream"); - + // This is mandatory to keep a reference on the video track // Else the video does not display in self.remoteVideoView remoteVideoTrack = stream.videoTracks.lastObject; @@ -357,6 +433,43 @@ - (void)peerConnection:(RTCPeerConnection *)peerConnection didRemoveStream:(RTCMediaStream *)stream { NSLog(@"[MXJingleCallStackCall] didRemoveStream"); + + dispatch_async(dispatch_get_main_queue(), ^{ + [self.delegate callStackCallDidHold:self]; + }); +} + +- (void)peerConnection:(RTCPeerConnection *)peerConnection didAddReceiver:(RTCRtpReceiver *)rtpReceiver streams:(NSArray *)mediaStreams +{ + NSLog(@"[MXJingleCallStackCall] didAddReceiver"); + + if ([rtpReceiver.track.kind isEqualToString:kRTCMediaStreamTrackKindVideo]) + { + // This is mandatory to keep a reference on the video track + // Else the video does not display in self.remoteVideoView + remoteVideoTrack = (RTCVideoTrack *)rtpReceiver.track; + + MXWeakify(self); + dispatch_async(dispatch_get_main_queue(), ^{ + MXStrongifyAndReturnIfNil(self); + + // Use self.remoteVideoView as a container of a RTCEAGLVideoView + self->remoteJingleVideoView = [[MXJingleVideoView alloc] initWithContainerView:self.remoteVideoView]; + [self->remoteVideoTrack addRenderer:self->remoteJingleVideoView]; + }); + } +} + +- (void)peerConnection:(RTCPeerConnection *)peerConnection didRemoveReceiver:(RTCRtpReceiver *)rtpReceiver +{ + NSLog(@"[MXJingleCallStackCall] didRemoveReceiver"); + + if ([rtpReceiver.track.kind isEqualToString:kRTCMediaStreamTrackKindAudio]) + { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.delegate callStackCallDidHold:self]; + }); + } } // Triggered when renegotiation is needed, for example the ICE has restarted. @@ -510,15 +623,55 @@ - (void)setCameraPosition:(AVCaptureDevicePosition)theCameraPosition self.captureController.cameraPosition = theCameraPosition; } +#pragma mark - Private methods +- (BOOL)isHoldOffer:(NSString *)sdpOffer +{ + NSString *keyword = @"m=audio"; + NSError *error = NULL; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:keyword + options:NSRegularExpressionCaseInsensitive + error:&error]; + NSUInteger numberOfAudioTracks = [regex numberOfMatchesInString:sdpOffer options:0 range:NSMakeRange(0, [sdpOffer length])]; + + keyword = @"m=video"; + error = NULL; + regex = [NSRegularExpression regularExpressionWithPattern:keyword + options:NSRegularExpressionCaseInsensitive + error:&error]; + NSUInteger numberOfVideoTracks = [regex numberOfMatchesInString:sdpOffer options:0 range:NSMakeRange(0, [sdpOffer length])]; + + if (numberOfAudioTracks == 0 && numberOfVideoTracks == 0) + { + // no audio or video tracks + return YES; + } + + keyword = @"a=inactive"; + error = NULL; + regex = [NSRegularExpression regularExpressionWithPattern:keyword + options:NSRegularExpressionCaseInsensitive + error:&error]; + NSUInteger numberOfInactiveTracks = [regex numberOfMatchesInString:sdpOffer options:0 range:NSMakeRange(0, [sdpOffer length])]; + + return (numberOfAudioTracks + numberOfVideoTracks) == numberOfInactiveTracks; +} -#pragma mark - Private methods - (RTCMediaConstraints *)mediaConstraints { return [[RTCMediaConstraints alloc] initWithMandatoryConstraints:@{ - @"OfferToReceiveAudio": @"true", - @"OfferToReceiveVideo": (isVideoCall ? @"true" : @"false") - } + kRTCMediaConstraintsOfferToReceiveAudio: kRTCMediaConstraintsValueTrue, + kRTCMediaConstraintsOfferToReceiveVideo: (isVideoCall ? kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse) + } + optionalConstraints:nil]; +} + +- (RTCMediaConstraints *)mediaConstraintsForHoldedCall +{ + return [[RTCMediaConstraints alloc] initWithMandatoryConstraints:@{ + kRTCMediaConstraintsOfferToReceiveAudio: kRTCMediaConstraintsValueFalse, + kRTCMediaConstraintsOfferToReceiveVideo: kRTCMediaConstraintsValueFalse + } optionalConstraints:nil]; } @@ -527,7 +680,11 @@ - (void)createLocalMediaStream // Set up audio localAudioTrack = [self createLocalAudioTrack]; - [peerConnection addTrack:localAudioTrack streamIds:@[@"ARDAMS"]]; + RTCRtpTransceiverInit *transceiverInit = [[RTCRtpTransceiverInit alloc] init]; + transceiverInit.direction = RTCRtpTransceiverDirectionSendRecv; + transceiverInit.streamIds = @[@"ARDAMS"]; + + [peerConnection addTransceiverWithTrack:localAudioTrack init:transceiverInit]; // And video if (isVideoCall) @@ -536,7 +693,7 @@ - (void)createLocalMediaStream // Create a video track and add it to the media stream if (localVideoTrack) { - [peerConnection addTrack:localVideoTrack streamIds:@[@"ARDAMS"]]; + [peerConnection addTransceiverWithTrack:localVideoTrack init:transceiverInit]; // Display the self view // Use selfVideoView as a container of a RTCEAGLVideoView From b4217e74a22d7cb49a02a41f660f80fc3f42daa7 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 27 Nov 2020 18:06:58 +0300 Subject: [PATCH 13/89] Support holding on CallKit --- MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m index fa1f610bb9..b26e836be2 100644 --- a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m +++ b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m @@ -112,7 +112,7 @@ - (void)startCall:(MXCall *)call update.remoteHandle = handle; update.localizedCallerName = contactIdentifier; update.hasVideo = call.isVideoCall; - update.supportsHolding = NO; + update.supportsHolding = YES; update.supportsGrouping = NO; update.supportsUngrouping = NO; update.supportsDTMF = NO; @@ -194,7 +194,7 @@ - (void)reportIncomingCall:(MXCall *)call { update.remoteHandle = handle; update.localizedCallerName = caller.displayname; update.hasVideo = call.isVideoCall; - update.supportsHolding = NO; + update.supportsHolding = YES; update.supportsGrouping = NO; update.supportsUngrouping = NO; update.supportsDTMF = NO; @@ -298,6 +298,16 @@ - (void)provider:(CXProvider *)provider performSetMutedCallAction:(CXSetMutedCal [action fulfill]; } +- (void)provider:(CXProvider *)provider performSetHeldCallAction:(CXSetHeldCallAction *)action +{ + MXCall *call = self.calls[action.callUUID]; + if (call) + { + [call hold:action.onHold]; + } + + [action fulfill]; +} #pragma mark - Private methods From a1eca7d03361352225c9c0a4ed97085031725307 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:32:01 +0300 Subject: [PATCH 14/89] Update MatrixSDK/VoIP/MXCall.m Co-authored-by: manuroe --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 13d1e886fa..84d32a831b 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -1046,7 +1046,7 @@ - (void)handleCallNegotiate:(MXEvent *)event [self->callStackCall handleAnswer:content.sessionDescription.sdp success:^{} failure:^(NSError *error) { - NSLog(@"[MXCall] handleCallEvent: ERROR: Cannot send handle negotiate answer. Error: %@\nEvent: %@", error, event); + NSLog(@"[MXCall] handleCallNegotiate: ERROR: Cannot send handle negotiate answer. Error: %@\nEvent: %@", error, event); [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; }]; } From fbd129900defee9f5de49394d8edbada4ba79241 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:32:09 +0300 Subject: [PATCH 15/89] Update MatrixSDK/VoIP/MXCall.m Co-authored-by: manuroe --- MatrixSDK/VoIP/MXCall.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 84d32a831b..af277b8d70 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -1114,7 +1114,8 @@ - (void)terminateWithReason:(MXEvent *)event MXCallHangupEventContent *content = [MXCallHangupEventContent modelFromJSON:event.content]; MXCallHangupReason reason = content.reasonType; - switch (reason) { + switch (reason) + { case MXCallHangupReasonUserHangup: if ([event.sender isEqualToString:callManager.mxSession.myUserId]) { From 129d1548e9363842e47dc61cdb79412e9741bd94 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:32:21 +0300 Subject: [PATCH 16/89] Update MatrixSDK/VoIP/MXCall.m Co-authored-by: manuroe --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index af277b8d70..74900c14d7 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -1037,7 +1037,7 @@ - (void)handleCallNegotiate:(MXEvent *)event }]; } failure:^(NSError * _Nonnull error) { - NSLog(@"[MXCall] handleOffer: ERROR: Couldn't handle negotiate offer. Error: %@", error); + NSLog(@"[MXCall] handleCallNegotiate: ERROR: Couldn't handle negotiate offer. Error: %@", error); [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; }]; } From 329bb5cc495395f72f34107e8cb32f1e6c5eef6c Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:32:29 +0300 Subject: [PATCH 17/89] Update MatrixSDK/VoIP/MXCall.m Co-authored-by: manuroe --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 74900c14d7..1e5cb20968 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -1032,7 +1032,7 @@ - (void)handleCallNegotiate:(MXEvent *)event [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; } failure:^(NSError * _Nonnull error) { - NSLog(@"[MXCall] negotiate answer: ERROR: Cannot create negotiate answer. Error: %@", error); + NSLog(@"[MXCall] handleCallNegotiate: negotiate answer: ERROR: Cannot create negotiate answer. Error: %@", error); [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; }]; } From 8aadbdff90dbc0fe3204d4000eb09345bc5825ba Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:32:38 +0300 Subject: [PATCH 18/89] Update MatrixSDK/VoIP/MXCall.m Co-authored-by: manuroe --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 1e5cb20968..aadaac605d 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -1028,7 +1028,7 @@ - (void)handleCallNegotiate:(MXEvent *)event @"lifetime": @(self->callManager.negotiateLifetime) }; [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallNegotiate content:content localEcho:nil success:nil failure:^(NSError *error) { - NSLog(@"[MXCall] negotiate answer: ERROR: Cannot send m.call.negotiate event."); + NSLog(@"[MXCall] handleCallNegotiate: negotiate answer: ERROR: Cannot send m.call.negotiate event."); [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; } failure:^(NSError * _Nonnull error) { From 51c25b7bdda0d5501707f6fbf343c3be0bca99f6 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:32:46 +0300 Subject: [PATCH 19/89] Update MatrixSDK/Utils/MXTools.m Co-authored-by: manuroe --- MatrixSDK/Utils/MXTools.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MatrixSDK/Utils/MXTools.m b/MatrixSDK/Utils/MXTools.m index b65a1e8d17..7e105cb9cd 100644 --- a/MatrixSDK/Utils/MXTools.m +++ b/MatrixSDK/Utils/MXTools.m @@ -314,7 +314,8 @@ + (MXCallHangupReasonString)callHangupReasonString:(MXCallHangupReason)reason { MXCallHangupReasonString string; - switch (reason) { + switch (reason) + { case MXCallHangupReasonUserHangup: string = kMXCallHangupReasonUserHangup; break; From 47dfaf6f7b0d877e4c54dc96f690350ed7f5c97a Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:33:10 +0300 Subject: [PATCH 20/89] Update MatrixSDK/JSONModels/Call/MXTurnServerResponse.h Co-authored-by: manuroe --- MatrixSDK/JSONModels/Call/MXTurnServerResponse.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/JSONModels/Call/MXTurnServerResponse.h b/MatrixSDK/JSONModels/Call/MXTurnServerResponse.h index 84626bb5f5..6dd197e99d 100644 --- a/MatrixSDK/JSONModels/Call/MXTurnServerResponse.h +++ b/MatrixSDK/JSONModels/Call/MXTurnServerResponse.h @@ -38,7 +38,7 @@ The URI scheme obeys to http://tools.ietf.org/html/rfc7064#section-3.1 and http://tools.ietf.org/html/rfc7065#section-3.1 */ -@property (nonatomic) NSArray *uris; +@property (nonatomic) NSArray *uris; /** Time To Live. The time is seconds this data is still valid. From f89bc307ee1b235cfb4a6153b5ff57a4c4d4627e Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:33:24 +0300 Subject: [PATCH 21/89] Update MatrixSDK/Utils/MXTools.m Co-authored-by: manuroe --- MatrixSDK/Utils/MXTools.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MatrixSDK/Utils/MXTools.m b/MatrixSDK/Utils/MXTools.m index 7e105cb9cd..d862059592 100644 --- a/MatrixSDK/Utils/MXTools.m +++ b/MatrixSDK/Utils/MXTools.m @@ -369,7 +369,8 @@ + (MXCallSessionDescriptionTypeString)callSessionDescriptionTypeString:(MXCallSe { MXCallSessionDescriptionTypeString string; - switch (type) { + switch (type) + { case MXCallSessionDescriptionTypeOffer: string = kMXCallSessionDescriptionTypeOffer; break; From 991d67547b04c1146f3692f22330c3db4248f4e8 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:33:44 +0300 Subject: [PATCH 22/89] Update MatrixSDK/VoIP/MXCall.m Co-authored-by: manuroe --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index aadaac605d..e033e72568 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -445,7 +445,7 @@ - (void)hold:(BOOL)hold } } failure:^(NSError *error) { - NSLog(@"[MXCall] callWithVideo: ERROR: Cannot send m.call.negotiate event."); + NSLog(@"[MXCall] hold: ERROR: Cannot send m.call.negotiate event."); [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; From 16fc8577f0621f410c395ca5287d0ebcddd0137f Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:37:14 +0300 Subject: [PATCH 23/89] Update MatrixSDK/VoIP/MXCall.m Co-authored-by: manuroe --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index e033e72568..23c1946761 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -1010,7 +1010,7 @@ - (void)handleCallNegotiate:(MXEvent *)event success:^{ MXStrongifyAndReturnIfNil(self); - // TODO: Get offer type from handleOffer and decide auto-acppet it or not + // TODO: Get offer type from handleOffer and decide auto-accept it or not // auto-accept negotiations for now [self->callStackCall createAnswer:^(NSString * _Nonnull sdpAnswer) { MXStrongifyAndReturnIfNil(self); From 58b4e10352e0fdb7f9778634ec1b30cd3208f73f Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:37:33 +0300 Subject: [PATCH 24/89] Update MatrixSDK/VoIP/MXCall.m Co-authored-by: manuroe --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 23c1946761..dbbcb2fe90 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -419,7 +419,7 @@ - (void)hold:(BOOL)hold [callStackCall hold:hold success:^(NSString * _Nonnull sdp) { MXStrongifyAndReturnIfNil(self); - NSLog(@"[MXCall] %@ offer created: %@", (hold ? @"Hold" : @"Resume"), sdp); + NSLog(@"[MXCall] hold: %@ offer created: %@", (hold ? @"Hold" : @"Resume"), sdp); // The call hold offer can sent to the HS NSMutableDictionary *content = [@{ From ca43747d1067ca6e0bec117d92615b28c1873fff Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:37:56 +0300 Subject: [PATCH 25/89] Update MatrixSDK/VoIP/MXCall.m Co-authored-by: manuroe --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index dbbcb2fe90..306e4bcb9b 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -786,7 +786,7 @@ - (void)handleCallInvite:(MXEvent *)event } } failure:^(NSError * _Nonnull error) { - NSLog(@"[MXCall] handleOffer: ERROR: Couldn't handle offer. Error: %@", error); + NSLog(@"[MXCall] handleCallInvite: ERROR: Couldn't handle offer. Error: %@", error); [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; }]; } failure:^(NSError *error) { From 3ffaaa958425406eeb2df5884a1d21de8cb3a5f4 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:38:09 +0300 Subject: [PATCH 26/89] Update MatrixSDK/VoIP/MXCall.m Co-authored-by: manuroe --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 306e4bcb9b..823f58b663 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -790,7 +790,7 @@ - (void)handleCallInvite:(MXEvent *)event [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; }]; } failure:^(NSError *error) { - NSLog(@"[MXCall] startCapturingMediaWithVideo: ERROR: Couldn't start capturing. Error: %@", error); + NSLog(@"[MXCall] handleCallInvite: startCapturingMediaWithVideo : ERROR: Couldn't start capturing. Error: %@", error); [self didEncounterError:error reason:MXCallHangupReasonUserMediaFailed]; }]; From 4037d179551f8bdd3776e50a2bed82a0a3effa56 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:38:22 +0300 Subject: [PATCH 27/89] Update MatrixSDK/VoIP/MXCall.m Co-authored-by: manuroe --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 823f58b663..97d4185b31 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -839,7 +839,7 @@ - (void)handleCallAnswer:(MXEvent *)event [self->callStackCall handleAnswer:content.answer.sdp success:^{} failure:^(NSError *error) { - NSLog(@"[MXCall] handleCallEvent: ERROR: Cannot send handle answer. Error: %@\nEvent: %@", error, event); + NSLog(@"[MXCall] handleCallAnswer: ERROR: Cannot send handle answer. Error: %@\nEvent: %@", error, event); self->selectedAnswer = nil; [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; }]; From 4cfd3a5d43028af731cf6f220864b2c136400790 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:39:26 +0300 Subject: [PATCH 28/89] Update MatrixSDK/VoIP/MXCall.m Co-authored-by: manuroe --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 97d4185b31..36b0b5f5e0 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -862,7 +862,7 @@ - (void)handleCallAnswer:(MXEvent *)event continueBlock(); } failure:^(NSError *error) { - NSLog(@"[MXCall] callWithVideo: ERROR: Cannot send m.call.select_answer event. Error: %@\n", error); + NSLog(@"[MXCall] handleCallAnswer: ERROR: Cannot send m.call.select_answer event. Error: %@\n", error); self->selectedAnswer = nil; [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; From b5782a0dbeb96cc2448fa73ec0e0a0a4df75e131 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:39:38 +0300 Subject: [PATCH 29/89] Update MatrixSDK/VoIP/MXCall.m Co-authored-by: manuroe --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 36b0b5f5e0..f42e6c9e4d 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -975,7 +975,7 @@ - (void)handleCallReject:(MXEvent *)event [self terminateWithReason:event]; } failure:^(NSError *error) { - NSLog(@"[MXCall] callWithVideo: ERROR: Cannot send m.call.select_answer event. Error: %@\n", error); + NSLog(@"[MXCall] handleCallReject: ERROR: Cannot send m.call.select_answer event. Error: %@\n", error); self->selectedAnswer = nil; [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; From bbea3de2fde0c745b0a89f9d6ccdf09b723117f6 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 12:42:20 +0300 Subject: [PATCH 30/89] Update MatrixSDK/VoIP/MXCall.m Co-authored-by: manuroe --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index f42e6c9e4d..392fbf1e74 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -1015,7 +1015,7 @@ - (void)handleCallNegotiate:(MXEvent *)event [self->callStackCall createAnswer:^(NSString * _Nonnull sdpAnswer) { MXStrongifyAndReturnIfNil(self); - NSLog(@"[MXCall] answer negotiation - Created SDP:\n%@", sdpAnswer); + NSLog(@"[MXCall] handleCallNegotiate: answer negotiation - Created SDP:\n%@", sdpAnswer); NSDictionary *content = @{ @"call_id": self.callId, From 6a3923bae83ec1ac42d65676f3b7d11acd47589a Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 14:03:10 +0300 Subject: [PATCH 31/89] Revert "Support holding on CallKit" This reverts commit b4217e74a22d7cb49a02a41f660f80fc3f42daa7. --- MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m index b26e836be2..fa1f610bb9 100644 --- a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m +++ b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m @@ -112,7 +112,7 @@ - (void)startCall:(MXCall *)call update.remoteHandle = handle; update.localizedCallerName = contactIdentifier; update.hasVideo = call.isVideoCall; - update.supportsHolding = YES; + update.supportsHolding = NO; update.supportsGrouping = NO; update.supportsUngrouping = NO; update.supportsDTMF = NO; @@ -194,7 +194,7 @@ - (void)reportIncomingCall:(MXCall *)call { update.remoteHandle = handle; update.localizedCallerName = caller.displayname; update.hasVideo = call.isVideoCall; - update.supportsHolding = YES; + update.supportsHolding = NO; update.supportsGrouping = NO; update.supportsUngrouping = NO; update.supportsDTMF = NO; @@ -298,16 +298,6 @@ - (void)provider:(CXProvider *)provider performSetMutedCallAction:(CXSetMutedCal [action fulfill]; } -- (void)provider:(CXProvider *)provider performSetHeldCallAction:(CXSetHeldCallAction *)action -{ - MXCall *call = self.calls[action.callUUID]; - if (call) - { - [call hold:action.onHold]; - } - - [action fulfill]; -} #pragma mark - Private methods From 9bdd7f5358e9e0113b6c381b247417cfe32b0940 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 14:04:12 +0300 Subject: [PATCH 32/89] Add nullable specifiers --- .../Call/Events/MXCallAnswerEventContent.h | 4 ++++ .../Call/Events/MXCallCandidatesEventContent.h | 4 ++++ .../JSONModels/Call/Events/MXCallEventContent.h | 12 ++++++++---- .../Call/Events/MXCallHangupEventContent.h | 8 ++++++-- .../Call/Events/MXCallInviteEventContent.h | 6 +++++- .../Call/Events/MXCallNegotiateEventContent.h | 4 ++++ .../Call/Events/MXCallRejectEventContent.h | 4 ++++ .../Call/Events/MXCallSelectAnswerEventContent.h | 4 ++++ 8 files changed, 39 insertions(+), 7 deletions(-) diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.h index f70066ea49..315fcdaed0 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.h @@ -18,6 +18,8 @@ #import "MXCallEventContent.h" #import "MXCallSessionDescription.h" +NS_ASSUME_NONNULL_BEGIN + /** `MXCallAnswerEventContent` represents the content of a m.call.answer event. */ @@ -34,3 +36,5 @@ @property (nonatomic) MXCallSessionDescription *answer; @end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.h index 26057c593c..c06ba5954b 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.h @@ -18,6 +18,8 @@ #import "MXCallEventContent.h" #import "MXCallCandidate.h" +NS_ASSUME_NONNULL_BEGIN + /** `MXCallCandidatesEventContent` represents the content of a m.call.candidates event. */ @@ -34,3 +36,5 @@ @property (nonatomic) NSArray *candidates; @end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.h index c6974e1c64..3cc17b7a57 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.h @@ -17,6 +17,8 @@ #import #import "MXJSONModel.h" +NS_ASSUME_NONNULL_BEGIN + /** Call version */ @@ -28,17 +30,17 @@ extern NSString *const kMXCallVersion; /** The version of the VoIP specification this message adheres to. Can be nil. @see `version`. */ -@property (nonatomic, copy) NSNumber *versionNumber; +@property (nonatomic, copy, nullable) NSNumber *versionNumber; /** The version of the VoIP specification this message adheres to. Can be nil. @see `version`. */ -@property (nonatomic, copy) NSString *versionString; +@property (nonatomic, copy, nullable) NSString *versionString; /** - The party id for the call event. + The party id for the call event. When a client participates in a new call, it generates a party_id for itself to use for the rest of the call. Parties in the call are identified by (user_id, party_id) tuple. Can be nil for older call versions. */ -@property (nonatomic, copy) NSString *partyId; +@property (nonatomic, copy, nullable) NSString *partyId; /// Parse base fields from the JSON /// @param JSONDictionary The JSON to be parsed @@ -48,3 +50,5 @@ extern NSString *const kMXCallVersion; - (NSString *)version; @end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.h index f3b19c2e76..31ea019409 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.h @@ -29,6 +29,8 @@ typedef NS_ENUM(NSInteger, MXCallHangupReason) typedef NSString * MXCallHangupReasonString NS_REFINED_FOR_SWIFT; +NS_ASSUME_NONNULL_BEGIN + FOUNDATION_EXPORT NSString *const kMXCallHangupReasonUserHangup; FOUNDATION_EXPORT NSString *const kMXCallHangupReasonIceFailed; FOUNDATION_EXPORT NSString *const kMXCallHangupReasonInviteTimeout; @@ -47,10 +49,10 @@ FOUNDATION_EXPORT NSString *const kMXCallHangupReasonUnknownError; @property (nonatomic, copy) NSString *callId; /** - The reason of the hangup event. Can be mapped to a MXCallHangupReason enum. + The reason of the hangup event. Can be mapped to a MXCallHangupReason enum. Can be nil for older call versions. @seealso reasonType */ -@property (nonatomic, copy) MXCallHangupReasonString reason; +@property (nonatomic, copy, nullable) MXCallHangupReasonString reason; /** Mapped reason of the hangup event. @@ -58,3 +60,5 @@ FOUNDATION_EXPORT NSString *const kMXCallHangupReasonUnknownError; @property (nonatomic, assign) MXCallHangupReason reasonType; @end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.h index ef67221f22..6f18dee99c 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.h @@ -18,6 +18,8 @@ #import "MXCallEventContent.h" #import "MXCallSessionDescription.h" +NS_ASSUME_NONNULL_BEGIN + /** `MXCallInviteEventContent` represents the content of a m.call.invite event. */ @@ -43,7 +45,7 @@ /** Target user id of the invite. Can be nil. Invites without an invitee defined to be intended for any member of the room (other than the sender). */ -@property (nonatomic, copy) NSString *invitee; +@property (nonatomic, copy, nullable) NSString *invitee; /** Indicate whether the invitation is for a video call. @@ -51,3 +53,5 @@ - (BOOL)isVideoCall; @end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h index 7c40802db4..729e9149bd 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h @@ -18,6 +18,8 @@ #import "MXCallEventContent.h" #import "MXCallSessionDescription.h" +NS_ASSUME_NONNULL_BEGIN + /** `MXCallNegotiateEventContent` represents the content of a m.call.negotiate event. */ @@ -46,3 +48,5 @@ - (BOOL)isVideoCall; @end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.h index e801ad6300..7abad71f0b 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.h @@ -17,6 +17,8 @@ #import #import "MXCallEventContent.h" +NS_ASSUME_NONNULL_BEGIN + /** `MXCallRejectEventContent` represents the content of a m.call.reject event. */ @@ -28,3 +30,5 @@ @property (nonatomic) NSString *callId; @end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.h index ab77ccf5d9..a5215580ce 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.h @@ -17,6 +17,8 @@ #import #import "MXCallEventContent.h" +NS_ASSUME_NONNULL_BEGIN + /** `MXCallSelectAnswerEventContent` represents the content of a m.call.select_answer event. */ @@ -33,3 +35,5 @@ @property (nonatomic) NSString *selectedPartyId; @end + +NS_ASSUME_NONNULL_END From 1d7c071f60c9652c9ea7b7c815f7958f7664d759 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 14:25:37 +0300 Subject: [PATCH 33/89] Update some doc --- MatrixSDK/VoIP/MXCall.h | 7 +++++++ MatrixSDK/VoIP/MXCall.m | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/MatrixSDK/VoIP/MXCall.h b/MatrixSDK/VoIP/MXCall.h index 787528ec70..3461d92706 100644 --- a/MatrixSDK/VoIP/MXCall.h +++ b/MatrixSDK/VoIP/MXCall.h @@ -122,8 +122,15 @@ extern NSString *const kMXCallStateDidChange; */ - (void)answer; +/** + Flag to indicate that the call can be holded. + */ @property (nonatomic, readonly) BOOL supportsHolding; +/** + Hold/unhold the call. The call must be connected to hold and must be already holded to unhold the call. + Please note that also remotely holded calls cannot be unholded. + */ - (void)hold:(BOOL)hold; /** diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 392fbf1e74..f5301ae895 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -450,7 +450,7 @@ - (void)hold:(BOOL)hold }]; } failure:^(NSError * _Nonnull error) { - NSLog(@"[MXCall] callWithVideo: ERROR: Cannot create %@ offer. Error: %@", (hold ? @"Hold" : @"Resume"), error); + NSLog(@"[MXCall] hold: ERROR: Cannot create %@ offer. Error: %@", (hold ? @"Hold" : @"Resume"), error); [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; }]; } @@ -501,7 +501,7 @@ - (void)hangupWithReason:(MXCallHangupReason)reason @"reason": [MXTools callHangupReasonString:reason] }; [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallHangup content:content localEcho:nil success:nil failure:^(NSError *error) { - NSLog(@"[MXCall] hangup: ERROR: Cannot send m.call.hangup event."); + NSLog(@"[MXCall] hangupWithReason: ERROR: Cannot send m.call.hangup event."); [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; From adf1a6391106ff7c86ddb8e2f697c1857fcfa55d Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 14:25:50 +0300 Subject: [PATCH 34/89] Remove old version check --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index f5301ae895..fc1c2cf734 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -380,7 +380,7 @@ - (BOOL)supportsHolding if (callInviteEventContent && selectedAnswer && [callInviteEventContent.version isEqualToString:kMXCallVersion]) { MXCallAnswerEventContent *content = [MXCallAnswerEventContent modelFromJSON:selectedAnswer.content]; - return [content.version isEqualToString:kMXCallVersion] || [content.version isEqualToString:@"0"]; + return [content.version isEqualToString:kMXCallVersion]; } return NO; } From bde23c43ec09164e18afe08a513446f3d2120e6b Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 30 Nov 2020 14:29:38 +0300 Subject: [PATCH 35/89] Fix warning --- MatrixSDK/VoIP/MXCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index fc1c2cf734..ca6217e538 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -338,7 +338,7 @@ - (void)answer }; [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallAnswer content:content localEcho:nil success:^(NSString *eventId){ // assume for now, this is the selected answer - selectedAnswer = [MXEvent modelFromJSON:@{ + self->selectedAnswer = [MXEvent modelFromJSON:@{ @"event_id": eventId, @"sender": self.callSignalingRoom.mxSession.myUserId, @"room_id": self.callSignalingRoom.roomId, From ebfa9d570b1ebb546681aa5dc5ba72019ac4da72 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 2 Dec 2020 22:04:54 +0300 Subject: [PATCH 36/89] Add hold support for calls --- MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h | 4 + MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m | 32 ++++++++ MatrixSDK/VoIP/MXCall.h | 21 +++++- MatrixSDK/VoIP/MXCall.m | 91 ++++++++++++++--------- MatrixSDK/VoIP/MXCallManager.m | 17 +++++ 5 files changed, 128 insertions(+), 37 deletions(-) diff --git a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h index 206cfb8184..913ba9a55e 100644 --- a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h +++ b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h @@ -47,6 +47,10 @@ API_AVAILABLE(ios(10.0)) - (void)reportCall:(MXCall *)call startedConnectingAtDate:(nullable NSDate *)date; - (void)reportCall:(MXCall *)call connectedAtDate:(nullable NSDate *)date; +/// Update hold support for the given call. +/// @param call The call +- (void)updateSupportsHoldingForCall:(MXCall *)call; + /** Tell about support of CallKit by the OS diff --git a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m index fa1f610bb9..5bd5cffecb 100644 --- a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m +++ b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m @@ -220,6 +220,27 @@ - (void)reportCall:(MXCall *)call connectedAtDate:(nullable NSDate *)date [self.provider reportOutgoingCallWithUUID:call.callUUID connectedAtDate:date]; } +- (void)updateSupportsHoldingForCall:(MXCall *)call +{ + NSUUID *callUUID = call.callUUID; + + if (!self.calls[callUUID]) + { + // This call is not managed by the CallKit, ignore. + return; + } + + BOOL supportsHolding = call.supportsHolding; + + CXCallUpdate *update = [[CXCallUpdate alloc] init]; + // Doc says "Any property that is not set will be ignored" for CXCallUpdate. + // So we don't have to set other properties for the update. + update.supportsHolding = supportsHolding; + + [self.provider reportCallWithUUID:callUUID updated:update]; + NSLog(@"[MXCallKitAdapter] updateSupportsHoldingForCall, call(%@) updated to: %u", call.callId, supportsHolding); +} + + (BOOL)callKitAvailable { #if TARGET_IPHONE_SIMULATOR @@ -274,6 +295,17 @@ - (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAct [action fulfill]; } +- (void)provider:(CXProvider *)provider performSetHeldCallAction:(CXSetHeldCallAction *)action +{ + MXCall *call = self.calls[action.callUUID]; + if (call) + { + [call hold:action.onHold]; + } + + [action fulfill]; +} + - (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action { MXCall *call = self.calls[action.callUUID]; diff --git a/MatrixSDK/VoIP/MXCall.h b/MatrixSDK/VoIP/MXCall.h index 3461d92706..fab07141ee 100644 --- a/MatrixSDK/VoIP/MXCall.h +++ b/MatrixSDK/VoIP/MXCall.h @@ -47,8 +47,8 @@ typedef NS_ENUM(NSUInteger, MXCallState) MXCallStateConnecting, MXCallStateConnected, - MXCallStateHolded, - MXCallStateRemoteHolded, + MXCallStateOnHold, + MXCallStateRemotelyOnHold, MXCallStateEnded, MXCallStateInviteExpired, @@ -75,6 +75,12 @@ typedef NS_ENUM(NSInteger, MXCallEndReason) */ extern NSString *const kMXCallStateDidChange; +/** + Posted when a `MXCall` object has changed its status to support holding. + The notification object is the `MXKCall` object representing the call. + */ +extern NSString *const kMXCallSupportsHoldingStatusDidChange; + @protocol MXCallDelegate; /** @@ -133,6 +139,11 @@ extern NSString *const kMXCallStateDidChange; */ - (void)hold:(BOOL)hold; +/** + Call is on hold, locally or remotely. + */ +@property (nonatomic, readonly) BOOL isOnHold; + /** Hang up a call in progress or reject an incoming call. */ @@ -290,6 +301,12 @@ extern NSString *const kMXCallStateDidChange; @optional +/** + Tells the delegate that status of the call to support holding has changed. + @param call the instance that changes, + */ +- (void)callSupportsHoldingStatusDidChange:(MXCall *)call; + /** Tells the delegate an error occured. The call cannot be established. diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index ca6217e538..ff4bb38a55 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -33,6 +33,7 @@ #pragma mark - Constants definitions NSString *const kMXCallStateDidChange = @"kMXCallStateDidChange"; +NSString *const kMXCallSupportsHoldingStatusDidChange = @"kMXCallSupportsHoldingStatusDidChange"; @interface MXCall () { @@ -80,13 +81,13 @@ @interface MXCall () Cache for self.calleeId. */ NSString *calleeId; - - /** - Selected answer for this call. Only exists for outgoing calls and after an answer (an accept or reject) received. - */ - MXEvent *selectedAnswer; } +/** + Selected answer for this call. Can be nil. + */ +@property (nonatomic, strong) MXEvent *selectedAnswer; + @end @implementation MXCall @@ -338,7 +339,7 @@ - (void)answer }; [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallAnswer content:content localEcho:nil success:^(NSString *eventId){ // assume for now, this is the selected answer - self->selectedAnswer = [MXEvent modelFromJSON:@{ + self.selectedAnswer = [MXEvent modelFromJSON:@{ @"event_id": eventId, @"sender": self.callSignalingRoom.mxSession.myUserId, @"room_id": self.callSignalingRoom.roomId, @@ -377,9 +378,9 @@ - (void)answer - (BOOL)supportsHolding { - if (callInviteEventContent && selectedAnswer && [callInviteEventContent.version isEqualToString:kMXCallVersion]) + if (callInviteEventContent && _selectedAnswer && [callInviteEventContent.version isEqualToString:kMXCallVersion]) { - MXCallAnswerEventContent *content = [MXCallAnswerEventContent modelFromJSON:selectedAnswer.content]; + MXCallAnswerEventContent *content = [MXCallAnswerEventContent modelFromJSON:_selectedAnswer.content]; return [content.version isEqualToString:kMXCallVersion]; } return NO; @@ -395,7 +396,7 @@ - (void)hold:(BOOL)hold if (hold) { - if (_state == MXCallStateHolded || _state == MXCallStateRemoteHolded) + if (_state == MXCallStateOnHold || _state == MXCallStateRemotelyOnHold) { // already holded return; @@ -403,7 +404,7 @@ - (void)hold:(BOOL)hold } else { - if (_state == MXCallStateRemoteHolded) + if (_state == MXCallStateRemotelyOnHold) { // remotely holded calls cannot be unholded return; @@ -437,7 +438,7 @@ - (void)hold:(BOOL)hold if (hold) { - [self setState:MXCallStateHolded reason:nil]; + [self setState:MXCallStateOnHold reason:nil]; } else { @@ -455,6 +456,11 @@ - (void)hold:(BOOL)hold }]; } +- (BOOL)isOnHold +{ + return _state == MXCallStateOnHold || _state == MXCallStateRemotelyOnHold; +} + - (void)hangup { NSLog(@"[MXCall] hangup"); @@ -515,7 +521,29 @@ - (void)hangupWithReason:(MXCallHangupReason)reason } } -#pragma marl - Properties +#pragma mark - Properties + +- (void)setSelectedAnswer:(MXEvent *)selectedAnswer +{ + if (_selectedAnswer == selectedAnswer) + { + // already the same, ignore it + return; + } + + _selectedAnswer = selectedAnswer; + + if ([_delegate respondsToSelector:@selector(callSupportsHoldingStatusDidChange:)]) + { + [_delegate callSupportsHoldingStatusDidChange:self]; + } + + // Broadcast the new call status + [[NSNotificationCenter defaultCenter] postNotificationName:kMXCallSupportsHoldingStatusDidChange + object:self + userInfo:nil]; +} + - (void)setState:(MXCallState)state reason:(MXEvent *)event { NSLog(@"[MXCall] setState. old: %@. New: %@", @(_state), @(state)); @@ -523,7 +551,7 @@ - (void)setState:(MXCallState)state reason:(MXEvent *)event // Manage call duration if (MXCallStateConnected == state) { - if (_state != MXCallStateHolded && _state != MXCallStateRemoteHolded) + if (_state != MXCallStateOnHold && _state != MXCallStateRemotelyOnHold) { // Set the start point callConnectedDate = [NSDate date]; @@ -712,7 +740,7 @@ - (void)callStackCallDidHold:(id)callStackCall { if (self.state == MXCallStateConnected) { - [self setState:MXCallStateRemoteHolded reason:nil]; + [self setState:MXCallStateRemotelyOnHold reason:nil]; } } @@ -732,7 +760,7 @@ - (void)callStackCall:(id)callStackCall onError:(NSError *)erro - (void)callStackCallDidConnect:(id)callStackCall { - if (self.state == MXCallStateConnecting || self.state == MXCallStateRemoteHolded) + if (self.state == MXCallStateConnecting || self.state == MXCallStateRemotelyOnHold) { [self setState:MXCallStateConnected reason:nil]; } @@ -814,7 +842,7 @@ - (void)handleCallAnswer:(MXEvent *)event // Listen to answer event only for call we are making, not receiving if (!_isIncoming) { - if (selectedAnswer) + if (_selectedAnswer) { // there is already a selected answer, ignore this one return; @@ -831,7 +859,7 @@ - (void)handleCallAnswer:(MXEvent *)event } // mark this as the selected one - selectedAnswer = event; + self.selectedAnswer = event; void(^continueBlock)(void) = ^{ // Let's the stack finalise the connection @@ -840,7 +868,7 @@ - (void)handleCallAnswer:(MXEvent *)event success:^{} failure:^(NSError *error) { NSLog(@"[MXCall] handleCallAnswer: ERROR: Cannot send handle answer. Error: %@\nEvent: %@", error, event); - self->selectedAnswer = nil; + self.selectedAnswer = nil; [self didEncounterError:error reason:MXCallHangupReasonIceFailed]; }]; }; @@ -863,7 +891,7 @@ - (void)handleCallAnswer:(MXEvent *)event } failure:^(NSError *error) { NSLog(@"[MXCall] handleCallAnswer: ERROR: Cannot send m.call.select_answer event. Error: %@\n", error); - self->selectedAnswer = nil; + self.selectedAnswer = nil; [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; } @@ -890,19 +918,12 @@ - (void)handleCallSelectAnswer:(MXEvent *)event if (_isIncoming) { MXCallSelectAnswerEventContent *content = [MXCallSelectAnswerEventContent modelFromJSON:event.content]; - if (selectedAnswer) - { - // check assumed selectedAnswer is really the selected one - MXCallAnswerEventContent *selectedAnswerContent = [MXCallAnswerEventContent modelFromJSON:selectedAnswer.content]; - if (![selectedAnswerContent.partyId isEqualToString:content.selectedPartyId]) - { - // a different answer is selected - selectedAnswer = nil; - } - } if (![content.selectedPartyId isEqualToString:self.partyId]) { - // Else this event means that the call has been answered (accepted/rejected) by another user/device + // a different answer is selected, also our assumption was wrong + self.selectedAnswer = nil; + + // This means that the call has been answered (accepted/rejected) by another user/device [self onCallAnsweredElsewhere]; } } @@ -942,7 +963,7 @@ - (void)handleCallReject:(MXEvent *)event // Listen to answer event only for call we are making, not receiving if (!_isIncoming) { - if (selectedAnswer) + if (_selectedAnswer) { // there is already a selected answer, ignore this one return; @@ -958,7 +979,7 @@ - (void)handleCallReject:(MXEvent *)event if (_state != MXCallStateEnded) { MXCallRejectEventContent *content = [MXCallRejectEventContent modelFromJSON:event.content]; - selectedAnswer = event; + self.selectedAnswer = event; NSDictionary *selectAnswerContent = @{ @"call_id": self.callId, @@ -976,7 +997,7 @@ - (void)handleCallReject:(MXEvent *)event } failure:^(NSError *error) { NSLog(@"[MXCall] handleCallReject: ERROR: Cannot send m.call.select_answer event. Error: %@\n", error); - self->selectedAnswer = nil; + self.selectedAnswer = nil; [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; } @@ -1062,9 +1083,9 @@ - (BOOL)canHandleNegotiationEvent:(MXEvent *)event } // outgoing call, check the event coming from the same user with the selected answer - if (selectedAnswer && [selectedAnswer.sender isEqualToString:event.sender]) + if (_selectedAnswer && [_selectedAnswer.sender isEqualToString:event.sender]) { - MXCallEventContent *selectedAnswerContent = [MXCallEventContent modelFromJSON:selectedAnswer.content]; + MXCallEventContent *selectedAnswerContent = [MXCallEventContent modelFromJSON:_selectedAnswer.content]; MXCallNegotiateEventContent *content = [MXCallNegotiateEventContent modelFromJSON:event.content]; // return if user-id and party-id matches diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index fee5e679c7..01d5063fa5 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -102,6 +102,11 @@ - (instancetype)initWithMatrixSession:(MXSession *)mxSession andCallStack:(id Date: Thu, 3 Dec 2020 15:42:05 +0300 Subject: [PATCH 37/89] Report local call hold to CallKit --- MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h | 1 + MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m | 19 +++++++++++++++++++ MatrixSDK/VoIP/MXCallManager.m | 3 +++ 3 files changed, 23 insertions(+) diff --git a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h index 913ba9a55e..4c3ab7a686 100644 --- a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h +++ b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.h @@ -46,6 +46,7 @@ API_AVAILABLE(ios(10.0)) - (void)reportCall:(MXCall *)call startedConnectingAtDate:(nullable NSDate *)date; - (void)reportCall:(MXCall *)call connectedAtDate:(nullable NSDate *)date; +- (void)reportCall:(MXCall *)call onHold:(BOOL)onHold; /// Update hold support for the given call. /// @param call The call diff --git a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m index 5bd5cffecb..8c3fc29d7d 100644 --- a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m +++ b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m @@ -218,6 +218,25 @@ - (void)reportCall:(MXCall *)call startedConnectingAtDate:(nullable NSDate *)dat - (void)reportCall:(MXCall *)call connectedAtDate:(nullable NSDate *)date { [self.provider reportOutgoingCallWithUUID:call.callUUID connectedAtDate:date]; + [self reportCall:call onHold:NO]; +} + +- (void)reportCall:(MXCall *)call onHold:(BOOL)onHold +{ + NSUUID *callUUID = call.callUUID; + + if (!self.calls[callUUID]) + { + // This call is not managed by the CallKit, ignore. + return; + } + + CXSetHeldCallAction *holdCallAction = [[CXSetHeldCallAction alloc] initWithCallUUID:callUUID onHold:onHold]; + CXTransaction *transaction = [[CXTransaction alloc] initWithAction:holdCallAction]; + + [self.callController requestTransaction:transaction completion:^(NSError *error) { + + }]; } - (void)updateSupportsHoldingForCall:(MXCall *)call diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 01d5063fa5..1daa018175 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -519,6 +519,9 @@ - (void)handleCallStateDidChangeNotification:(NSNotification *)notification case MXCallStateConnected: [self.callKitAdapter reportCall:call connectedAtDate:nil]; break; + case MXCallStateOnHold: + [self.callKitAdapter reportCall:call onHold:YES]; + break; case MXCallStateEnded: [self.callKitAdapter endCall:call]; break; From 0bcaf922691f77ea8666102b0537f31442c3da16 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 3 Dec 2020 23:58:54 +0300 Subject: [PATCH 38/89] Send VoIP analytics events --- MatrixSDK/MXEnumConstants.h | 14 ++++++++++++++ MatrixSDK/MXEnumConstants.m | 10 ++++++++++ MatrixSDK/VoIP/MXCall.m | 26 +++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/MatrixSDK/MXEnumConstants.h b/MatrixSDK/MXEnumConstants.h index 87a45f64df..3b2862ae88 100644 --- a/MatrixSDK/MXEnumConstants.h +++ b/MatrixSDK/MXEnumConstants.h @@ -254,3 +254,17 @@ FOUNDATION_EXPORT NSString *const kMXAnalyticsStatsCategory; // The number of room the user is in FOUNDATION_EXPORT NSString *const kMXAnalyticsStatsRooms; +// VoIP + +/** + Overall VoIP category. + */ +FOUNDATION_EXPORT NSString *const kMXAnalyticsVoipCategory; + +// VoIP event names +FOUNDATION_EXPORT NSString *const kMXAnalyticsVoipNameCallError; +FOUNDATION_EXPORT NSString *const kMXAnalyticsVoipNameCallHangup; +FOUNDATION_EXPORT NSString *const kMXAnalyticsVoipNameCallEnded; +FOUNDATION_EXPORT NSString *const kMXAnalyticsVoipNamePlaceCall; +FOUNDATION_EXPORT NSString *const kMXAnalyticsVoipNamePlaceConferenceCall; +FOUNDATION_EXPORT NSString *const kMXAnalyticsVoipNameReceiveCall; diff --git a/MatrixSDK/MXEnumConstants.m b/MatrixSDK/MXEnumConstants.m index ba95e6d065..91627444d6 100644 --- a/MatrixSDK/MXEnumConstants.m +++ b/MatrixSDK/MXEnumConstants.m @@ -93,3 +93,13 @@ NSString *const kMXAnalyticsStatsCategory = @"stats"; NSString *const kMXAnalyticsStatsRooms = @"rooms"; + +// VoIP +NSString *const kMXAnalyticsVoipCategory = @"voip"; + +NSString *const kMXAnalyticsVoipNameCallError = @"callError"; +NSString *const kMXAnalyticsVoipNameCallHangup = @"callHangup"; +NSString *const kMXAnalyticsVoipNameCallEnded = @"callEnded"; +NSString *const kMXAnalyticsVoipNamePlaceCall = @"placeCall"; +NSString *const kMXAnalyticsVoipNamePlaceConferenceCall = @"placeConferenceCall"; +NSString *const kMXAnalyticsVoipNameReceiveCall = @"receiveCall"; diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index ca6217e538..7e930e40d7 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -23,6 +23,8 @@ #import "MXEvent.h" #import "MXSession.h" #import "MXTools.h" +#import "MXSDKOptions.h" +#import "MXEnumConstants.h" #import "MXCallInviteEventContent.h" #import "MXCallAnswerEventContent.h" @@ -242,6 +244,12 @@ - (void)callWithVideo:(BOOL)video callStackCall.audioToSpeaker = _isVideoCall; [self setState:MXCallStateWaitLocalMedia reason:nil]; + + NSString *eventName = _isConferenceCall ? kMXAnalyticsVoipNamePlaceConferenceCall : kMXAnalyticsVoipNamePlaceCall; + + [[MXSDKOptions sharedInstance].analyticsDelegate trackValue:@(video) + category:kMXAnalyticsVoipCategory + name:eventName]; MXWeakify(self); [callStackCall startCapturingMediaWithVideo:video success:^() { @@ -500,7 +508,11 @@ - (void)hangupWithReason:(MXCallHangupReason)reason @"party_id": self.partyId, @"reason": [MXTools callHangupReasonString:reason] }; - [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallHangup content:content localEcho:nil success:nil failure:^(NSError *error) { + [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallHangup content:content localEcho:nil success:^(NSString *eventId) { + [[MXSDKOptions sharedInstance].analyticsDelegate trackValue:@(reason) + category:kMXAnalyticsVoipCategory + name:kMXAnalyticsVoipNameCallHangup]; + } failure:^(NSError *error) { NSLog(@"[MXCall] hangupWithReason: ERROR: Cannot send m.call.hangup event."); [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; }]; @@ -539,6 +551,10 @@ - (void)setState:(MXCallState)state reason:(MXEvent *)event // Store the total duration totalCallDuration = self.duration; + + [[MXSDKOptions sharedInstance].analyticsDelegate trackValue:@(_endReason) + category:kMXAnalyticsVoipCategory + name:kMXAnalyticsVoipNameCallEnded]; } else if (MXCallStateInviteSent == state) { @@ -764,6 +780,10 @@ - (void)handleCallInvite:(MXEvent *)event // Store if it is voice or video call _isVideoCall = callInviteEventContent.isVideoCall; + + [[MXSDKOptions sharedInstance].analyticsDelegate trackValue:@(_isVideoCall) + category:kMXAnalyticsVoipCategory + name:kMXAnalyticsVoipNameReceiveCall]; // Set up the default audio route callStackCall.audioToSpeaker = _isVideoCall; @@ -1176,6 +1196,10 @@ - (void)didEncounterError:(NSError *)error reason:(MXCallHangupReason)reason if ([_delegate respondsToSelector:@selector(call:didEncounterError:reason:)]) { [_delegate call:self didEncounterError:error reason:reason]; + + [[MXSDKOptions sharedInstance].analyticsDelegate trackValue:@(reason) + category:kMXAnalyticsVoipCategory + name:kMXAnalyticsVoipNameCallError]; } else { From 0017c92fddfa00db3f24b4b81d386a6c9bfd89cc Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 3 Dec 2020 23:59:54 +0300 Subject: [PATCH 39/89] Update CHANGES.rst --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index be61aeab80..b712d06cca 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,7 @@ Changes to be released in next version * 🙌 Improvements - * + * Send VoIP analytics events (vector-im/element-ios/issues/3855). 🐛 Bugfix * From 46f25df4c15230b5d377a4d6b438bc25d74ba3cd Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 4 Dec 2020 00:08:09 +0300 Subject: [PATCH 40/89] Update CHANGES.rst --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index be61aeab80..1bba92d66e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,7 @@ Changes to be released in next version * 🙌 Improvements - * + * Add hold support for CallKit calls (vector-im/element-ios/issues/3834). 🐛 Bugfix * From dbab752b654d26e9fd02bf3b89f9de1ca85e72da Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 9 Dec 2020 15:10:33 +0300 Subject: [PATCH 41/89] Fix video call with web, refactor hold support --- .../VoIP/Jingle/MXJingleCallStackCall.m | 89 +++++++------------ 1 file changed, 30 insertions(+), 59 deletions(-) diff --git a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m index 443357e4cf..a8685b2d51 100644 --- a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m +++ b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m @@ -27,6 +27,8 @@ #import "MXJingleCameraCaptureController.h" #import +NSString *const kMXJingleCallWebRTCMainStreamID = @"ARDAMS"; + @interface MXJingleCallStackCall () { /** @@ -302,14 +304,6 @@ - (void)createAnswer:(void (^)(NSString *))success failure:(void (^)(NSError *)) if (!error) { success(sdp.sdp); - if ([self isHoldOffer:sdp.sdp]) - { - [self.delegate callStackCallDidHold:self]; - } - else - { - [self.delegate callStackCallDidConnect:self]; - } } else { @@ -396,7 +390,7 @@ - (void)handleAnswer:(NSString *)sdp success:(void (^)(void))success failure:(vo }]; } -#pragma mark - RTCPeerConnectionDelegate delegate +#pragma mark - RTCPeerConnectionDelegate // Triggered when the SignalingState changed. - (void)peerConnection:(RTCPeerConnection *)peerConnection @@ -411,21 +405,9 @@ - (void)peerConnection:(RTCPeerConnection *)peerConnection { NSLog(@"[MXJingleCallStackCall] didAddStream"); - // This is mandatory to keep a reference on the video track - // Else the video does not display in self.remoteVideoView - remoteVideoTrack = stream.videoTracks.lastObject; - - if (remoteVideoTrack) - { - MXWeakify(self); - dispatch_async(dispatch_get_main_queue(), ^{ - MXStrongifyAndReturnIfNil(self); - - // Use self.remoteVideoView as a container of a RTCEAGLVideoView - self->remoteJingleVideoView = [[MXJingleVideoView alloc] initWithContainerView:self.remoteVideoView]; - [self->remoteVideoTrack addRenderer:self->remoteJingleVideoView]; - }); - } + dispatch_async(dispatch_get_main_queue(), ^{ + [self.delegate callStackCallDidConnect:self]; + }); } // Triggered when a remote peer close a stream. @@ -434,6 +416,8 @@ - (void)peerConnection:(RTCPeerConnection *)peerConnection { NSLog(@"[MXJingleCallStackCall] didRemoveStream"); + // TODO: Check this stream has the main stream ID (kMXJingleCallWebRTCMainStreamID) after all platforms decided to use the same stream id. + dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate callStackCallDidHold:self]; }); @@ -463,13 +447,6 @@ - (void)peerConnection:(RTCPeerConnection *)peerConnection didAddReceiver:(RTCRt - (void)peerConnection:(RTCPeerConnection *)peerConnection didRemoveReceiver:(RTCRtpReceiver *)rtpReceiver { NSLog(@"[MXJingleCallStackCall] didRemoveReceiver"); - - if ([rtpReceiver.track.kind isEqualToString:kRTCMediaStreamTrackKindAudio]) - { - dispatch_async(dispatch_get_main_queue(), ^{ - [self.delegate callStackCallDidHold:self]; - }); - } } // Triggered when renegotiation is needed, for example the ICE has restarted. @@ -625,36 +602,32 @@ - (void)setCameraPosition:(AVCaptureDevicePosition)theCameraPosition #pragma mark - Private methods +// Not used for now, will be in future - (BOOL)isHoldOffer:(NSString *)sdpOffer { - NSString *keyword = @"m=audio"; - NSError *error = NULL; - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:keyword - options:NSRegularExpressionCaseInsensitive - error:&error]; - NSUInteger numberOfAudioTracks = [regex numberOfMatchesInString:sdpOffer options:0 range:NSMakeRange(0, [sdpOffer length])]; - - keyword = @"m=video"; - error = NULL; - regex = [NSRegularExpression regularExpressionWithPattern:keyword - options:NSRegularExpressionCaseInsensitive - error:&error]; - NSUInteger numberOfVideoTracks = [regex numberOfMatchesInString:sdpOffer options:0 range:NSMakeRange(0, [sdpOffer length])]; - + NSUInteger numberOfAudioTracks = [self numberOfMatchesOfKeyword:@"m=audio" inString:sdpOffer]; + NSUInteger numberOfVideoTracks = [self numberOfMatchesOfKeyword:@"m=video" inString:sdpOffer]; + if (numberOfAudioTracks == 0 && numberOfVideoTracks == 0) { // no audio or video tracks return YES; } + + NSUInteger numberOfInactiveTracks = [self numberOfMatchesOfKeyword:@"a=inactive" inString:sdpOffer]; - keyword = @"a=inactive"; - error = NULL; - regex = [NSRegularExpression regularExpressionWithPattern:keyword + return (numberOfAudioTracks + numberOfVideoTracks) == numberOfInactiveTracks; +} + +- (NSUInteger)numberOfMatchesOfKeyword:(NSString *)keyword inString:(NSString *)string +{ + NSError *error = NULL; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:keyword options:NSRegularExpressionCaseInsensitive error:&error]; - NSUInteger numberOfInactiveTracks = [regex numberOfMatchesInString:sdpOffer options:0 range:NSMakeRange(0, [sdpOffer length])]; - - return (numberOfAudioTracks + numberOfVideoTracks) == numberOfInactiveTracks; + return [regex numberOfMatchesInString:string + options:0 + range:NSMakeRange(0, [string length])]; } - (RTCMediaConstraints *)mediaConstraints @@ -680,11 +653,7 @@ - (void)createLocalMediaStream // Set up audio localAudioTrack = [self createLocalAudioTrack]; - RTCRtpTransceiverInit *transceiverInit = [[RTCRtpTransceiverInit alloc] init]; - transceiverInit.direction = RTCRtpTransceiverDirectionSendRecv; - transceiverInit.streamIds = @[@"ARDAMS"]; - - [peerConnection addTransceiverWithTrack:localAudioTrack init:transceiverInit]; + [peerConnection addTrack:localAudioTrack streamIds:@[kMXJingleCallWebRTCMainStreamID]]; // And video if (isVideoCall) @@ -693,7 +662,7 @@ - (void)createLocalMediaStream // Create a video track and add it to the media stream if (localVideoTrack) { - [peerConnection addTransceiverWithTrack:localVideoTrack init:transceiverInit]; + [peerConnection addTrack:localVideoTrack streamIds:@[kMXJingleCallWebRTCMainStreamID]]; // Display the self view // Use selfVideoView as a container of a RTCEAGLVideoView @@ -716,7 +685,8 @@ - (RTCAudioTrack*)createLocalAudioTrack { RTCMediaConstraints *mediaConstraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:nil optionalConstraints:nil]; RTCAudioSource *localAudioSource = [peerConnectionFactory audioSourceWithConstraints:mediaConstraints]; - return [peerConnectionFactory audioTrackWithSource:localAudioSource trackId:@"ARDAMSa0"]; + NSString *trackId = [NSString stringWithFormat:@"%@a0", kMXJingleCallWebRTCMainStreamID]; + return [peerConnectionFactory audioTrackWithSource:localAudioSource trackId:trackId]; } - (RTCVideoTrack*)createLocalVideoTrack @@ -725,7 +695,8 @@ - (RTCVideoTrack*)createLocalVideoTrack self.videoCapturer = [self createVideoCapturerWithVideoSource:localVideoSource]; - return [peerConnectionFactory videoTrackWithSource:localVideoSource trackId:@"ARDAMSv0"]; + NSString *trackId = [NSString stringWithFormat:@"%@v0", kMXJingleCallWebRTCMainStreamID]; + return [peerConnectionFactory videoTrackWithSource:localVideoSource trackId:trackId]; } - (RTCVideoCapturer*)createVideoCapturerWithVideoSource:(RTCVideoSource*)videoSource From 08f179b12374a605e9db1f5f1b9331fff0f8a314 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 9 Dec 2020 15:16:17 +0300 Subject: [PATCH 42/89] Update CHANGES.rst --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 7860e28bc3..0a730557d8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,7 @@ Changes to be released in next version * MXTaggedEvents: Expose "m.tagged_events" according to [MSC2437](https://github.com/matrix-org/matrix-doc/pull/2437). * Send VoIP analytics events (vector-im/element-ios/issues/3855). * Add hold support for CallKit calls (vector-im/element-ios/issues/3834). + * Fix video call with web (vector-im/element-ios/issues/3862). 🐛 Bugfix * From 4e2a14c64add217a4ee99afc4d6334ed92d5e7a5 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 9 Dec 2020 16:15:33 +0300 Subject: [PATCH 43/89] Change stream id --- MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m index a8685b2d51..a97d89977f 100644 --- a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m +++ b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m @@ -27,7 +27,7 @@ #import "MXJingleCameraCaptureController.h" #import -NSString *const kMXJingleCallWebRTCMainStreamID = @"ARDAMS"; +NSString *const kMXJingleCallWebRTCMainStreamID = @"userMedia"; @interface MXJingleCallStackCall () { From 28e67ae42f737b00a57b33441fd043764e4716c8 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 9 Dec 2020 17:31:14 +0300 Subject: [PATCH 44/89] Change call hold consideration --- .../VoIP/Jingle/MXJingleCallStackCall.m | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m index a97d89977f..0b1d610d25 100644 --- a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m +++ b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m @@ -312,6 +312,9 @@ - (void)createAnswer:(void (^)(NSString *))success failure:(void (^)(NSError *)) }); + // check we can consider this call as held, after setting local description + [self checkTheCallIsOnHold]; + }]; } else @@ -415,12 +418,6 @@ - (void)peerConnection:(RTCPeerConnection *)peerConnection didRemoveStream:(RTCMediaStream *)stream { NSLog(@"[MXJingleCallStackCall] didRemoveStream"); - - // TODO: Check this stream has the main stream ID (kMXJingleCallWebRTCMainStreamID) after all platforms decided to use the same stream id. - - dispatch_async(dispatch_get_main_queue(), ^{ - [self.delegate callStackCallDidHold:self]; - }); } - (void)peerConnection:(RTCPeerConnection *)peerConnection didAddReceiver:(RTCRtpReceiver *)rtpReceiver streams:(NSArray *)mediaStreams @@ -602,7 +599,34 @@ - (void)setCameraPosition:(AVCaptureDevicePosition)theCameraPosition #pragma mark - Private methods -// Not used for now, will be in future +- (void)checkTheCallIsOnHold +{ + NSArray *activeReceivers = [self->peerConnection.transceivers filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(RTC_OBJC_TYPE(RTCRtpTransceiver) *transceiver, NSDictionary * _Nullable bindings) { + + RTCRtpTransceiverDirection direction = RTCRtpTransceiverDirectionStopped; + if ([transceiver currentDirection:&direction]) + { + if (direction == RTCRtpTransceiverDirectionInactive || + direction == RTCRtpTransceiverDirectionSendOnly || + direction == RTCRtpTransceiverDirectionStopped) + { + return NO; + } + } + + return YES; + }]]; + + // if there is no active receivers left, we can say this call is holded + if (activeReceivers.count == 0) + { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.delegate callStackCallDidHold:self]; + }); + } +} + +// Not used for now, may be in future - (BOOL)isHoldOffer:(NSString *)sdpOffer { NSUInteger numberOfAudioTracks = [self numberOfMatchesOfKeyword:@"m=audio" inString:sdpOffer]; From aecf5bb247b0fbfe83e8d0bf86df9e400ab2ad16 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 9 Dec 2020 18:40:26 +0300 Subject: [PATCH 45/89] Change method name --- MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m index 0b1d610d25..b138931e24 100644 --- a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m +++ b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m @@ -313,7 +313,7 @@ - (void)createAnswer:(void (^)(NSString *))success failure:(void (^)(NSError *)) }); // check we can consider this call as held, after setting local description - [self checkTheCallIsOnHold]; + [self checkTheCallIsRemotelyOnHold]; }]; } @@ -599,7 +599,7 @@ - (void)setCameraPosition:(AVCaptureDevicePosition)theCameraPosition #pragma mark - Private methods -- (void)checkTheCallIsOnHold +- (void)checkTheCallIsRemotelyOnHold { NSArray *activeReceivers = [self->peerConnection.transceivers filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(RTC_OBJC_TYPE(RTCRtpTransceiver) *transceiver, NSDictionary * _Nullable bindings) { From b39ff4847157b568cffa4d7b07f967d74cb6ed92 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 9 Dec 2020 18:43:49 +0300 Subject: [PATCH 46/89] Change delegate method name --- MatrixSDK/VoIP/CallStack/MXCallStackCall.h | 4 ++-- MatrixSDK/VoIP/MXCall.m | 2 +- MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/MatrixSDK/VoIP/CallStack/MXCallStackCall.h b/MatrixSDK/VoIP/CallStack/MXCallStackCall.h index cecc068347..383dd5cd17 100644 --- a/MatrixSDK/VoIP/CallStack/MXCallStackCall.h +++ b/MatrixSDK/VoIP/CallStack/MXCallStackCall.h @@ -231,11 +231,11 @@ NS_ASSUME_NONNULL_BEGIN - (void)callStackCallDidConnect:(id)callStackCall; /** - Tells the delegate that connection was holded + Tells the delegate that connection was held by the remote peer @param callStackCall the corresponding instance. */ -- (void)callStackCallDidHold:(id)callStackCall; +- (void)callStackCallDidRemotelyHold:(id)callStackCall; @end diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 8ad555dfa5..90b0e44a2d 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -752,7 +752,7 @@ - (void)sendLocalIceCandidates } } -- (void)callStackCallDidHold:(id)callStackCall +- (void)callStackCallDidRemotelyHold:(id)callStackCall { if (self.state == MXCallStateConnected) { diff --git a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m index b138931e24..29c60ce8f1 100644 --- a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m +++ b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m @@ -617,11 +617,11 @@ - (void)checkTheCallIsRemotelyOnHold return YES; }]]; - // if there is no active receivers left, we can say this call is holded if (activeReceivers.count == 0) { + // if there is no active receivers left, we can say this call is holded dispatch_async(dispatch_get_main_queue(), ^{ - [self.delegate callStackCallDidHold:self]; + [self.delegate callStackCallDidRemotelyHold:self]; }); } } From 2002d4de57d35f04d1a05722c5499e71e0c9e579 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 9 Dec 2020 18:44:42 +0300 Subject: [PATCH 47/89] Change to recvonly --- MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m index 29c60ce8f1..b7b5b0a572 100644 --- a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m +++ b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m @@ -607,7 +607,7 @@ - (void)checkTheCallIsRemotelyOnHold if ([transceiver currentDirection:&direction]) { if (direction == RTCRtpTransceiverDirectionInactive || - direction == RTCRtpTransceiverDirectionSendOnly || + direction == RTCRtpTransceiverDirectionRecvOnly || // remote party can set a hold tone with 'sendonly' direction == RTCRtpTransceiverDirectionStopped) { return NO; @@ -619,7 +619,7 @@ - (void)checkTheCallIsRemotelyOnHold if (activeReceivers.count == 0) { - // if there is no active receivers left, we can say this call is holded + // if there is no active receivers (on the other party) left, we can say this call is holded dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate callStackCallDidRemotelyHold:self]; }); From aa9c750ec13b51e71c852c4e837d4375728204ba Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 9 Dec 2020 18:45:23 +0300 Subject: [PATCH 48/89] Do not rely on didAddStream on unhold --- .../VoIP/Jingle/MXJingleCallStackCall.m | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m index b7b5b0a572..99db18ba75 100644 --- a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m +++ b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m @@ -407,10 +407,6 @@ - (void)peerConnection:(RTCPeerConnection *)peerConnection didAddStream:(RTCMediaStream *)stream { NSLog(@"[MXJingleCallStackCall] didAddStream"); - - dispatch_async(dispatch_get_main_queue(), ^{ - [self.delegate callStackCallDidConnect:self]; - }); } // Triggered when a remote peer close a stream. @@ -624,6 +620,13 @@ - (void)checkTheCallIsRemotelyOnHold [self.delegate callStackCallDidRemotelyHold:self]; }); } + else + { + // otherwise we can say this call resumed after a remote hold + dispatch_async(dispatch_get_main_queue(), ^{ + [self.delegate callStackCallDidConnect:self]; + }); + } } // Not used for now, may be in future From 2dc5b66802fe0e9168ba1b08ee3635e9ae2a07c9 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 10 Dec 2020 15:36:05 +0300 Subject: [PATCH 49/89] Create classes and enums, refactor enum string names --- MatrixSDK.xcodeproj/project.pbxproj | 36 ++++++++ .../Contrib/Swift/JSONModels/MXEvent.swift | 6 +- .../Swift/JSONModels/MXJSONModels.swift | 39 +++++++-- .../Call/Events/MXCallAnswerEventContent.h | 7 +- .../Call/Events/MXCallAnswerEventContent.m | 1 - .../Events/MXCallCandidatesEventContent.h | 7 +- .../Events/MXCallCandidatesEventContent.m | 1 - .../Call/Events/MXCallEventContent.h | 5 ++ .../Call/Events/MXCallEventContent.m | 1 + .../Call/Events/MXCallHangupEventContent.h | 19 ++-- .../Call/Events/MXCallHangupEventContent.m | 15 ++-- .../Call/Events/MXCallInviteEventContent.h | 7 +- .../Call/Events/MXCallInviteEventContent.m | 1 - .../Call/Events/MXCallNegotiateEventContent.h | 7 +- .../Call/Events/MXCallNegotiateEventContent.m | 1 - .../Call/Events/MXCallRejectEventContent.h | 7 +- .../Call/Events/MXCallRejectEventContent.m | 1 - .../MXCallRejectReplacementEventContent.h | 70 +++++++++++++++ .../MXCallRejectReplacementEventContent.m | 70 +++++++++++++++ .../Call/Events/MXCallReplacesEventContent.h | 54 ++++++++++++ .../Call/Events/MXCallReplacesEventContent.m | 38 ++++++++ .../Events/MXCallSelectAnswerEventContent.h | 7 +- .../Events/MXCallSelectAnswerEventContent.m | 1 - .../JSONModels/Call/Events/MXUserModel.h | 44 ++++++++++ .../JSONModels/Call/Events/MXUserModel.m | 35 ++++++++ .../Call/MXCallSessionDescription.h | 8 +- .../Call/MXCallSessionDescription.m | 8 +- MatrixSDK/JSONModels/MXEvent.h | 4 + MatrixSDK/JSONModels/MXEvent.m | 2 + MatrixSDK/MatrixSDK.h | 11 +++ MatrixSDK/Utils/MXTools.h | 4 + MatrixSDK/Utils/MXTools.m | 87 ++++++++++++++----- MatrixSDK/VoIP/MXCall.m | 8 +- MatrixSDK/VoIP/MXCallManager.m | 22 ++++- 34 files changed, 532 insertions(+), 102 deletions(-) create mode 100644 MatrixSDK/JSONModels/Call/Events/MXCallRejectReplacementEventContent.h create mode 100644 MatrixSDK/JSONModels/Call/Events/MXCallRejectReplacementEventContent.m create mode 100644 MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.h create mode 100644 MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m create mode 100644 MatrixSDK/JSONModels/Call/Events/MXUserModel.h create mode 100644 MatrixSDK/JSONModels/Call/Events/MXUserModel.m diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 66068d4a85..6e02fb4126 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -1218,6 +1218,18 @@ EC976610255D4988000C36EF /* MXCallRejectEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC97660F255D4988000C36EF /* MXCallRejectEventContent.m */; }; EC976611255D4988000C36EF /* MXCallRejectEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC97660F255D4988000C36EF /* MXCallRejectEventContent.m */; }; ECAE7AEC24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = ECAE7AEB24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m */; }; + ECE36EFF25822BE300122124 /* MXCallReplacesEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE36EFD25822BE300122124 /* MXCallReplacesEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ECE36F0025822BE300122124 /* MXCallReplacesEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE36EFD25822BE300122124 /* MXCallReplacesEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ECE36F0125822BE300122124 /* MXCallReplacesEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = ECE36EFE25822BE300122124 /* MXCallReplacesEventContent.m */; }; + ECE36F0225822BE300122124 /* MXCallReplacesEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = ECE36EFE25822BE300122124 /* MXCallReplacesEventContent.m */; }; + ECE36F0925822CA900122124 /* MXCallRejectReplacementEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE36F0725822CA900122124 /* MXCallRejectReplacementEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ECE36F0A25822CA900122124 /* MXCallRejectReplacementEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE36F0725822CA900122124 /* MXCallRejectReplacementEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ECE36F0B25822CA900122124 /* MXCallRejectReplacementEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = ECE36F0825822CA900122124 /* MXCallRejectReplacementEventContent.m */; }; + ECE36F0C25822CA900122124 /* MXCallRejectReplacementEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = ECE36F0825822CA900122124 /* MXCallRejectReplacementEventContent.m */; }; + ECE36F1325823C8400122124 /* MXUserModel.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE36F1125823C8400122124 /* MXUserModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ECE36F1425823C8400122124 /* MXUserModel.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE36F1125823C8400122124 /* MXUserModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ECE36F1525823C8400122124 /* MXUserModel.m in Sources */ = {isa = PBXBuildFile; fileRef = ECE36F1225823C8400122124 /* MXUserModel.m */; }; + ECE36F1625823C8400122124 /* MXUserModel.m in Sources */ = {isa = PBXBuildFile; fileRef = ECE36F1225823C8400122124 /* MXUserModel.m */; }; ECFFA970255ED32700706454 /* MXCallNegotiateEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = ECFFA96F255ED32700706454 /* MXCallNegotiateEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; ECFFA971255ED32700706454 /* MXCallNegotiateEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = ECFFA96F255ED32700706454 /* MXCallNegotiateEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; ECFFA977255ED34500706454 /* MXCallNegotiateEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = ECFFA976255ED34500706454 /* MXCallNegotiateEventContent.m */; }; @@ -1911,6 +1923,12 @@ EC976604255D496C000C36EF /* MXCallRejectEventContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCallRejectEventContent.h; sourceTree = ""; }; EC97660F255D4988000C36EF /* MXCallRejectEventContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCallRejectEventContent.m; sourceTree = ""; }; ECAE7AEB24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTTPAdditionalHeadersTests.m; sourceTree = ""; }; + ECE36EFD25822BE300122124 /* MXCallReplacesEventContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCallReplacesEventContent.h; sourceTree = ""; }; + ECE36EFE25822BE300122124 /* MXCallReplacesEventContent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCallReplacesEventContent.m; sourceTree = ""; }; + ECE36F0725822CA900122124 /* MXCallRejectReplacementEventContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCallRejectReplacementEventContent.h; sourceTree = ""; }; + ECE36F0825822CA900122124 /* MXCallRejectReplacementEventContent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCallRejectReplacementEventContent.m; sourceTree = ""; }; + ECE36F1125823C8400122124 /* MXUserModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXUserModel.h; sourceTree = ""; }; + ECE36F1225823C8400122124 /* MXUserModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXUserModel.m; sourceTree = ""; }; ECFFA96F255ED32700706454 /* MXCallNegotiateEventContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCallNegotiateEventContent.h; sourceTree = ""; }; ECFFA976255ED34500706454 /* MXCallNegotiateEventContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCallNegotiateEventContent.m; sourceTree = ""; }; ED2F344856EFFCA383E37B22 /* Pods-SDK-MatrixSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDK-MatrixSDK.release.xcconfig"; path = "Target Support Files/Pods-SDK-MatrixSDK/Pods-SDK-MatrixSDK.release.xcconfig"; sourceTree = ""; }; @@ -3370,6 +3388,12 @@ EC97660F255D4988000C36EF /* MXCallRejectEventContent.m */, ECFFA96F255ED32700706454 /* MXCallNegotiateEventContent.h */, ECFFA976255ED34500706454 /* MXCallNegotiateEventContent.m */, + ECE36EFD25822BE300122124 /* MXCallReplacesEventContent.h */, + ECE36EFE25822BE300122124 /* MXCallReplacesEventContent.m */, + ECE36F1125823C8400122124 /* MXUserModel.h */, + ECE36F1225823C8400122124 /* MXUserModel.m */, + ECE36F0725822CA900122124 /* MXCallRejectReplacementEventContent.h */, + ECE36F0825822CA900122124 /* MXCallRejectReplacementEventContent.m */, ); path = Events; sourceTree = ""; @@ -3443,6 +3467,7 @@ EC976605255D496D000C36EF /* MXCallRejectEventContent.h in Headers */, 324AAC74239913AD00380A66 /* MXKeyVerificationDone.h in Headers */, B146D47421A5945800D8C2C6 /* MXAntivirusScanStatus.h in Headers */, + ECE36F1325823C8400122124 /* MXUserModel.h in Headers */, 322691361E5EFF8700966A6E /* MXDeviceListOperationsPool.h in Headers */, 3281E8B719E42DFE00976E1A /* MXJSONModel.h in Headers */, 3284A5A01DB7C00600A09972 /* MXCryptoStore.h in Headers */, @@ -3521,6 +3546,7 @@ 327E9AE12285497100A98BC1 /* MXEventContentRelatesTo.h in Headers */, 32133025228BFA800070BA9B /* MXReactionCountChangeListener.h in Headers */, 321CFDFB2254E728004D31DF /* MXTransactionCancelCode.h in Headers */, + ECE36F0925822CA900122124 /* MXCallRejectReplacementEventContent.h in Headers */, 32A9770421626E5C00919CC0 /* MXServerNotices.h in Headers */, 021AFBA42179E91900742B2C /* MXEncryptedContentFile.h in Headers */, B146D4F021A5AF7F00D8C2C6 /* MXRealmEventScanMapper.h in Headers */, @@ -3646,6 +3672,7 @@ 322A51C31D9AC8FE00C8536D /* MXCryptoAlgorithms.h in Headers */, 32F945F61FAB83D900622468 /* MXIncomingRoomKeyRequest.h in Headers */, 327137271A24D50A00DB6757 /* MXMyUser.h in Headers */, + ECE36EFF25822BE300122124 /* MXCallReplacesEventContent.h in Headers */, B146D47C21A5958400D8C2C6 /* MXAntivirusScanStatusFormatter.h in Headers */, 3220093819EFA4C9008DE41D /* MXEventListener.h in Headers */, 327C3E4B23A39D91006183D1 /* MXAggregatedReferencesUpdater.h in Headers */, @@ -3797,6 +3824,7 @@ EC976586255C0127000C36EF /* MXTurnServerResponse.h in Headers */, B14EF2DF2397E90400758AF0 /* MXDecryptionResult.h in Headers */, B14EF2E02397E90400758AF0 /* MXCall.h in Headers */, + ECE36F0025822BE300122124 /* MXCallReplacesEventContent.h in Headers */, B14EF2E12397E90400758AF0 /* MXContentScanEncryptedBody.h in Headers */, B14EF2E22397E90400758AF0 /* MX3PidAddManager.h in Headers */, B14EF2E32397E90400758AF0 /* MXDecrypting.h in Headers */, @@ -3870,6 +3898,7 @@ B14EF3142397E90400758AF0 /* MXLoginTerms.h in Headers */, B14EF3152397E90400758AF0 /* MXIncomingRoomKeyRequestCancellation.h in Headers */, 325AD44023BE3E7500FF5277 /* MXCrossSigningInfo.h in Headers */, + ECE36F1425823C8400122124 /* MXUserModel.h in Headers */, B14EF3162397E90400758AF0 /* MXContentScanResult.h in Headers */, B14EF3172397E90400758AF0 /* MXRoomMembers.h in Headers */, B14EF3182397E90400758AF0 /* MXPushRuleSenderNotificationPermissionConditionChecker.h in Headers */, @@ -3921,6 +3950,7 @@ B14EF33A2397E90400758AF0 /* MXLoginPolicyData.h in Headers */, B14EF33B2397E90400758AF0 /* MXInvite3PID.h in Headers */, B14EF33C2397E90400758AF0 /* MXFileStore.h in Headers */, + ECE36F0A25822CA900122124 /* MXCallRejectReplacementEventContent.h in Headers */, B14EF33D2397E90400758AF0 /* MXRealmMediaScanStore.h in Headers */, B14EF33E2397E90400758AF0 /* MXOlmInboundGroupSession.h in Headers */, B14EF33F2397E90400758AF0 /* (null) in Headers */, @@ -4267,6 +4297,7 @@ 32792BDD2296B90A00F4FC9D /* MXAggregatedEditsUpdater.m in Sources */, 3259CD541DF860C300186944 /* MXRealmCryptoStore.m in Sources */, 321B41401E09937E009EEEC7 /* MXRoomSummary.m in Sources */, + ECE36F1525823C8400122124 /* MXUserModel.m in Sources */, 32CAB1081A91EA34008C5BB9 /* MXPushRuleRoomMemberCountConditionChecker.m in Sources */, 3245A7511AF7B2930001D8A7 /* MXCall.m in Sources */, 32CF439E2371AF9500907C56 /* MXWellknownIntegrations.m in Sources */, @@ -4339,6 +4370,7 @@ B17B2BDD2369FC81009D6650 /* MXUIKitBackgroundTask.m in Sources */, 3293C701214BBA4F009B3DDB /* MXPeekingRoomSummary.m in Sources */, C602B58C1F2268F700B67D87 /* MXRoom.swift in Sources */, + ECE36F0B25822CA900122124 /* MXCallRejectReplacementEventContent.m in Sources */, 32E402BA21C957D2004E87A6 /* MXOlmSession.m in Sources */, 32A151271DABB0CB00400192 /* MXMegolmDecryption.m in Sources */, 327A5F50239805F600ED6329 /* MXKeyVerificationKey.m in Sources */, @@ -4423,6 +4455,7 @@ 323F3F9320D3F0C700D26D6A /* MXRoomEventFilter.m in Sources */, 3275FD9921A6B53300B9C13D /* MXLoginPolicyData.m in Sources */, 3240969E1F9F751600DBA607 /* MXPushRuleSenderNotificationPermissionConditionChecker.m in Sources */, + ECE36F0125822BE300122124 /* MXCallReplacesEventContent.m in Sources */, 71DE22E01BC7C51200284153 /* MXReceiptData.m in Sources */, B14766B923D9D9420091F721 /* MXUsersTrustLevelSummary.m in Sources */, 323547D92226D5D600F15F94 /* MXWellKnownBaseConfig.m in Sources */, @@ -4651,6 +4684,7 @@ B14EF2022397E90400758AF0 /* MXEventUnsignedData.m in Sources */, 324AAC792399140D00380A66 /* MXKeyVerificationKey.m in Sources */, B14EF2032397E90400758AF0 /* MXMegolmEncryption.m in Sources */, + ECE36F1625823C8400122124 /* MXUserModel.m in Sources */, B14EF2042397E90400758AF0 /* MXIncomingRoomKeyRequest.m in Sources */, 32581DEB23C8C0C900832EAA /* MXUserTrustLevel.m in Sources */, B14EF2052397E90400758AF0 /* MX3PidAddManager.swift in Sources */, @@ -4730,6 +4764,7 @@ B14EF2412397E90400758AF0 /* MXRoomFilter.m in Sources */, B14EF2422397E90400758AF0 /* MXDeviceInfo.m in Sources */, B14EF2432397E90400758AF0 /* MXIncomingSASTransaction.m in Sources */, + ECE36F0C25822CA900122124 /* MXCallRejectReplacementEventContent.m in Sources */, B14EF2442397E90400758AF0 /* NSObject+sortedKeys.m in Sources */, B14EF2452397E90400758AF0 /* MXAggregatedReactionsUpdater.m in Sources */, B14EF2462397E90400758AF0 /* MXRoomMembers.m in Sources */, @@ -4775,6 +4810,7 @@ B14EF2622397E90400758AF0 /* MXAntivirusScanStatusFormatter.m in Sources */, 324AAC7C2399140D00380A66 /* MXKeyVerificationStart.m in Sources */, B14EF2632397E90400758AF0 /* MXReactionCountChange.m in Sources */, + ECE36F0225822BE300122124 /* MXCallReplacesEventContent.m in Sources */, 324AAC762399140D00380A66 /* MXKeyVerificationCancel.m in Sources */, 32AF928D240EA3880008A0FD /* MXSecretShareSend.m in Sources */, B14EF2642397E90400758AF0 /* MXKey.m in Sources */, diff --git a/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift b/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift index 530f367d98..dc00c1e1af 100644 --- a/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift +++ b/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift @@ -58,6 +58,8 @@ public enum MXEventType: Equatable, Hashable { case callHangup case callReject case callNegotiate + case callReplaces + case callRejectReplacement case reaction case receipt case roomTombStone @@ -102,6 +104,8 @@ public enum MXEventType: Equatable, Hashable { case .callHangup: return kMXEventTypeStringCallHangup case .callReject: return kMXEventTypeStringCallReject case .callNegotiate: return kMXEventTypeStringCallNegotiate + case .callReplaces: return kMXEventTypeStringCallReplaces + case .callRejectReplacement: return kMXEventTypeStringCallRejectReplacement case .reaction: return kMXEventTypeStringReaction case .receipt: return kMXEventTypeStringReceipt case .roomTombStone: return kMXEventTypeStringRoomTombStone @@ -122,7 +126,7 @@ public enum MXEventType: Equatable, Hashable { } public init(identifier: String) { - let events: [MXEventType] = [.roomName, .roomTopic, .roomAvatar, .roomMember, .roomCreate, .roomJoinRules, .roomPowerLevels, .roomAliases, .roomCanonicalAlias, .roomEncrypted, .roomEncryption, .roomGuestAccess, .roomHistoryVisibility, .roomKey, .roomForwardedKey, .roomKeyRequest, .roomMessage, .roomMessageFeedback, .roomRedaction, .roomThirdPartyInvite, .roomTag, .presence, .typing, .callInvite, .callCandidates, .callAnswer, .callSelectAnswer, .callHangup, .callReject, .callNegotiate, .receipt, .roomTombStone, .taggedEvents] + let events: [MXEventType] = [.roomName, .roomTopic, .roomAvatar, .roomMember, .roomCreate, .roomJoinRules, .roomPowerLevels, .roomAliases, .roomCanonicalAlias, .roomEncrypted, .roomEncryption, .roomGuestAccess, .roomHistoryVisibility, .roomKey, .roomForwardedKey, .roomKeyRequest, .roomMessage, .roomMessageFeedback, .roomRedaction, .roomThirdPartyInvite, .roomTag, .presence, .typing, .callInvite, .callCandidates, .callAnswer, .callSelectAnswer, .callHangup, .callReject, .callNegotiate, .callReplaces, .callRejectReplacement, .receipt, .roomTombStone, .taggedEvents] self = events.first(where: { $0.identifier == identifier }) ?? .custom(identifier) } } diff --git a/MatrixSDK/Contrib/Swift/JSONModels/MXJSONModels.swift b/MatrixSDK/Contrib/Swift/JSONModels/MXJSONModels.swift index e6e645a41f..b1ed78c0c3 100644 --- a/MatrixSDK/Contrib/Swift/JSONModels/MXJSONModels.swift +++ b/MatrixSDK/Contrib/Swift/JSONModels/MXJSONModels.swift @@ -125,17 +125,17 @@ public enum MXCallHangupReason: Equatable, Hashable { public var identifier: String { switch self { case .userHangup: - return kMXCallHangupReasonUserHangup + return kMXCallHangupReasonStringUserHangup case .iceFailed: - return kMXCallHangupReasonIceFailed + return kMXCallHangupReasonStringIceFailed case .inviteTimeout: - return kMXCallHangupReasonInviteTimeout + return kMXCallHangupReasonStringInviteTimeout case .iceTimeout: - return kMXCallHangupReasonIceTimeout + return kMXCallHangupReasonStringIceTimeout case .userMediaFailed: - return kMXCallHangupReasonUserMediaFailed + return kMXCallHangupReasonStringUserMediaFailed case .unknownError: - return kMXCallHangupReasonUnknownError + return kMXCallHangupReasonStringUnknownError } } @@ -145,3 +145,30 @@ public enum MXCallHangupReason: Equatable, Hashable { } } + +/// Call reject replacement reason +public enum MXCallRejectReplacementReason: Equatable, Hashable { + case declined + case failedRoomInvite + case failedCallInvite + case failedCall + + public var identifier: String { + switch self { + case .declined: + return kMXCallRejectReplacementReasonStringDeclined + case .failedRoomInvite: + return kMXCallRejectReplacementReasonStringFailedRoomInvite + case .failedCallInvite: + return kMXCallRejectReplacementReasonStringFailedCallInvite + case .failedCall: + return kMXCallRejectReplacementReasonStringFailedCall + } + } + + public init(identifier: String) { + let reasons: [MXCallRejectReplacementReason] = [.declined, .failedRoomInvite, .failedCallInvite, .failedCall] + self = reasons.first(where: { $0.identifier == identifier }) ?? .declined + } + +} diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.h index 315fcdaed0..2428752578 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.h @@ -21,15 +21,10 @@ NS_ASSUME_NONNULL_BEGIN /** - `MXCallAnswerEventContent` represents the content of a m.call.answer event. + `MXCallAnswerEventContent` represents the content of an `m.call.answer` event. */ @interface MXCallAnswerEventContent : MXCallEventContent -/** - A unique identifier for the call. - */ -@property (nonatomic) NSString *callId; - /** The session description. */ diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.m index ec0e4d9283..67127dc8db 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.m @@ -24,7 +24,6 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary if (callAnswerEventContent) { [callAnswerEventContent parseJSON:JSONDictionary]; - MXJSONModelSetString(callAnswerEventContent.callId, JSONDictionary[@"call_id"]); MXJSONModelSetMXJSONModel(callAnswerEventContent.answer, MXCallSessionDescription, JSONDictionary[@"answer"]); } diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.h index c06ba5954b..2a05457137 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.h @@ -21,15 +21,10 @@ NS_ASSUME_NONNULL_BEGIN /** - `MXCallCandidatesEventContent` represents the content of a m.call.candidates event. + `MXCallCandidatesEventContent` represents the content of an `m.call.candidates` event. */ @interface MXCallCandidatesEventContent : MXCallEventContent -/** - The ID of the call this event relates to. - */ -@property (nonatomic) NSString *callId; - /** Array of object describing the candidates (@see MXCallCandidate). */ diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.m index 8b6dd6c03e..c3a555eae2 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallCandidatesEventContent.m @@ -24,7 +24,6 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary if (callCandidatesEventContent) { [callCandidatesEventContent parseJSON:JSONDictionary]; - MXJSONModelSetString(callCandidatesEventContent.callId, JSONDictionary[@"call_id"]); MXJSONModelSetMXJSONModelArray(callCandidatesEventContent.candidates, MXCallCandidate, JSONDictionary[@"candidates"]); } diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.h index 3cc17b7a57..cbf9f4cae3 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.h @@ -27,6 +27,11 @@ extern NSString *const kMXCallVersion; /// Base class for event contents of call events. @interface MXCallEventContent : MXJSONModel +/** + A unique identifier for the call. + */ +@property (nonatomic) NSString *callId; + /** The version of the VoIP specification this message adheres to. Can be nil. @see `version`. */ diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m index b46f0887f5..b4c2238f43 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m @@ -35,6 +35,7 @@ + (void)initialize - (void)parseJSON:(NSDictionary *)JSONDictionary { + MXJSONModelSetString(self.callId, JSONDictionary[@"call_id"]); MXJSONModelSetNumber(self.versionNumber, JSONDictionary[@"version"]); MXJSONModelSetString(self.versionString, JSONDictionary[@"version"]); MXJSONModelSetString(self.partyId, JSONDictionary[@"party_id"]); diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.h index 31ea019409..412472dd7f 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.h @@ -31,23 +31,18 @@ typedef NSString * MXCallHangupReasonString NS_REFINED_FOR_SWIFT; NS_ASSUME_NONNULL_BEGIN -FOUNDATION_EXPORT NSString *const kMXCallHangupReasonUserHangup; -FOUNDATION_EXPORT NSString *const kMXCallHangupReasonIceFailed; -FOUNDATION_EXPORT NSString *const kMXCallHangupReasonInviteTimeout; -FOUNDATION_EXPORT NSString *const kMXCallHangupReasonIceTimeout; -FOUNDATION_EXPORT NSString *const kMXCallHangupReasonUserMediaFailed; -FOUNDATION_EXPORT NSString *const kMXCallHangupReasonUnknownError; +FOUNDATION_EXPORT NSString *const kMXCallHangupReasonStringUserHangup; +FOUNDATION_EXPORT NSString *const kMXCallHangupReasonStringIceFailed; +FOUNDATION_EXPORT NSString *const kMXCallHangupReasonStringInviteTimeout; +FOUNDATION_EXPORT NSString *const kMXCallHangupReasonStringIceTimeout; +FOUNDATION_EXPORT NSString *const kMXCallHangupReasonStringUserMediaFailed; +FOUNDATION_EXPORT NSString *const kMXCallHangupReasonStringUnknownError; /** - `MXCallHangupEventContent` represents the content of a m.call.hangup event. + `MXCallHangupEventContent` represents the content of an `m.call.hangup` event. */ @interface MXCallHangupEventContent : MXCallEventContent -/** - A unique identifier for the call. - */ -@property (nonatomic, copy) NSString *callId; - /** The reason of the hangup event. Can be mapped to a MXCallHangupReason enum. Can be nil for older call versions. @seealso reasonType diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.m index 366ce765f3..cfe8c01dba 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallHangupEventContent.m @@ -17,12 +17,12 @@ #import "MXCallHangupEventContent.h" #import "MXTools.h" -NSString *const kMXCallHangupReasonUserHangup = @"user_hangup"; -NSString *const kMXCallHangupReasonIceFailed = @"ice_failed"; -NSString *const kMXCallHangupReasonInviteTimeout = @"invite_timeout"; -NSString *const kMXCallHangupReasonIceTimeout = @"ice_timeout"; -NSString *const kMXCallHangupReasonUserMediaFailed = @"user_media_failed"; -NSString *const kMXCallHangupReasonUnknownError = @"unknown_error"; +NSString *const kMXCallHangupReasonStringUserHangup = @"user_hangup"; +NSString *const kMXCallHangupReasonStringIceFailed = @"ice_failed"; +NSString *const kMXCallHangupReasonStringInviteTimeout = @"invite_timeout"; +NSString *const kMXCallHangupReasonStringIceTimeout = @"ice_timeout"; +NSString *const kMXCallHangupReasonStringUserMediaFailed = @"user_media_failed"; +NSString *const kMXCallHangupReasonStringUnknownError = @"unknown_error"; @implementation MXCallHangupEventContent @@ -32,11 +32,10 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary if (callHangupEventContent) { [callHangupEventContent parseJSON:JSONDictionary]; - MXJSONModelSetString(callHangupEventContent.callId, JSONDictionary[@"call_id"]); MXJSONModelSetString(callHangupEventContent.reason, JSONDictionary[@"reason"]); if (!callHangupEventContent.reason) { - callHangupEventContent.reason = kMXCallHangupReasonUserHangup; + callHangupEventContent.reason = kMXCallHangupReasonStringUserHangup; } } diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.h index 6f18dee99c..f325ebb83f 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.h @@ -21,15 +21,10 @@ NS_ASSUME_NONNULL_BEGIN /** - `MXCallInviteEventContent` represents the content of a m.call.invite event. + `MXCallInviteEventContent` represents the content of an `m.call.invite` event. */ @interface MXCallInviteEventContent : MXCallEventContent -/** - A unique identifier for the call. - */ -@property (nonatomic) NSString *callId; - /** The session description. */ diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.m index e7ddaefd2c..3a0bafe1eb 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.m @@ -24,7 +24,6 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary if (callInviteEventContent) { [callInviteEventContent parseJSON:JSONDictionary]; - MXJSONModelSetString(callInviteEventContent.callId, JSONDictionary[@"call_id"]); MXJSONModelSetMXJSONModel(callInviteEventContent.offer, MXCallSessionDescription, JSONDictionary[@"offer"]); MXJSONModelSetUInteger(callInviteEventContent.lifetime, JSONDictionary[@"lifetime"]); MXJSONModelSetString(callInviteEventContent.invitee, JSONDictionary[@"invitee"]); diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h index 729e9149bd..48a0019601 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h @@ -21,15 +21,10 @@ NS_ASSUME_NONNULL_BEGIN /** - `MXCallNegotiateEventContent` represents the content of a m.call.negotiate event. + `MXCallNegotiateEventContent` represents the content of an `m.call.negotiate` event. */ @interface MXCallNegotiateEventContent : MXCallEventContent -/** - A unique identifier for the call. - */ -@property (nonatomic) NSString *callId; - /** The session description. */ diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m index 5017554305..a2bc1edb0e 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m @@ -24,7 +24,6 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary if (callNegotiateEventContent) { [callNegotiateEventContent parseJSON:JSONDictionary]; - MXJSONModelSetString(callNegotiateEventContent.callId, JSONDictionary[@"call_id"]); MXJSONModelSetMXJSONModel(callNegotiateEventContent.sessionDescription, MXCallSessionDescription, JSONDictionary[@"description"]); MXJSONModelSetUInteger(callNegotiateEventContent.lifetime, JSONDictionary[@"lifetime"]); } diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.h index 7abad71f0b..6866e5e076 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.h @@ -20,15 +20,10 @@ NS_ASSUME_NONNULL_BEGIN /** - `MXCallRejectEventContent` represents the content of a m.call.reject event. + `MXCallRejectEventContent` represents the content of an `m.call.reject` event. */ @interface MXCallRejectEventContent : MXCallEventContent -/** - A unique identifier for the call. - */ -@property (nonatomic) NSString *callId; - @end NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.m index 9b0f21742d..684f0ef333 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallRejectEventContent.m @@ -24,7 +24,6 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary if (callRejectEventContent) { [callRejectEventContent parseJSON:JSONDictionary]; - MXJSONModelSetString(callRejectEventContent.callId, JSONDictionary[@"call_id"]); } return callRejectEventContent; diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallRejectReplacementEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallRejectReplacementEventContent.h new file mode 100644 index 0000000000..84bb1dd0ab --- /dev/null +++ b/MatrixSDK/JSONModels/Call/Events/MXCallRejectReplacementEventContent.h @@ -0,0 +1,70 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "MXCallHangupEventContent.h" + +typedef NS_ENUM(NSInteger, MXCallRejectReplacementReason) +{ + MXCallRejectReplacementReasonDeclined, + MXCallRejectReplacementReasonFailedRoomInvite, + MXCallRejectReplacementReasonFailedCallInvite, + MXCallRejectReplacementReasonFailedCall +} NS_REFINED_FOR_SWIFT; + +typedef NSString * MXCallRejectReplacementReasonString NS_REFINED_FOR_SWIFT; + +NS_ASSUME_NONNULL_BEGIN + +FOUNDATION_EXPORT NSString *const kMXCallRejectReplacementReasonStringDeclined; +FOUNDATION_EXPORT NSString *const kMXCallRejectReplacementReasonStringFailedRoomInvite; +FOUNDATION_EXPORT NSString *const kMXCallRejectReplacementReasonStringFailedCallInvite; +FOUNDATION_EXPORT NSString *const kMXCallRejectReplacementReasonStringFailedCall; + +/** + `MXCallRejectReplacementEventContent` represents the content of an `m.call.reject_replacement` event. + */ +@interface MXCallRejectReplacementEventContent : MXCallEventContent + +/** + An identifier for the call replacement itself, generated by the transferor. + */ +@property (nonatomic) NSString *replacementId; + +/** + The reason a replacement is being rejected. + */ +@property (nonatomic) MXCallRejectReplacementReasonString reason; + +/** + Mapped reason of the reject event. + */ +@property (nonatomic) MXCallRejectReplacementReason reasonType; + +/** + May be present if `reason` is failedCall, in which case it gives the reason field from the replacement call's hangup event. + @seealso reasonType + */ +@property (nonatomic, nullable) MXCallHangupReasonString callFailureReason; + +/** + Mapped call failure reason of the reject event. + */ +@property (nonatomic, assign) MXCallHangupReason callFailureReasonType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallRejectReplacementEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallRejectReplacementEventContent.m new file mode 100644 index 0000000000..12eb4dc411 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/Events/MXCallRejectReplacementEventContent.m @@ -0,0 +1,70 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXCallRejectReplacementEventContent.h" +#import "MXTools.h" + +NSString *const kMXCallRejectReplacementReasonStringDeclined = @"declined"; +NSString *const kMXCallRejectReplacementReasonStringFailedRoomInvite = @"failed_room_invite"; +NSString *const kMXCallRejectReplacementReasonStringFailedCallInvite = @"failed_call_invite"; +NSString *const kMXCallRejectReplacementReasonStringFailedCall = @"failed_call"; + +@implementation MXCallRejectReplacementEventContent + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXCallRejectReplacementEventContent *content = [[MXCallRejectReplacementEventContent alloc] init]; + + if (content) + { + [content parseJSON:JSONDictionary]; + MXJSONModelSetString(content.replacementId, JSONDictionary[@"replacement_id"]); + MXJSONModelSetString(content.reason, JSONDictionary[@"reason"]); + if (!content.reason) + { + content.reason = kMXCallRejectReplacementReasonStringDeclined; + } + MXJSONModelSetString(content.callFailureReason, JSONDictionary[@"call_failure_reason"]); + if (!content.callFailureReason) + { + content.callFailureReason = kMXCallHangupReasonStringUserHangup; + } + } + + return content; +} + +- (MXCallRejectReplacementReason)reasonType +{ + return [MXTools callRejectReplacementReason:self.reason]; +} + +- (void)setReasonType:(MXCallRejectReplacementReason)reasonType +{ + self.reason = [MXTools callRejectReplacementReasonString:reasonType]; +} + +- (MXCallHangupReason)callFailureReasonType +{ + return [MXTools callHangupReason:self.callFailureReason]; +} + +- (void)setCallFailureReasonType:(MXCallHangupReason)callFailureReasonType +{ + self.callFailureReason = [MXTools callHangupReasonString:callFailureReasonType]; +} + +@end diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.h new file mode 100644 index 0000000000..ba5eae9db8 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.h @@ -0,0 +1,54 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXCallEventContent.h" +#import "MXUserModel.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + `MXCallReplacesEventContent` represents the content of an `m.call.replaces` event. + */ +@interface MXCallReplacesEventContent : MXCallEventContent + +/** + An identifier for the call replacement itself, generated by the transferor. + */ +@property (nonatomic) NSString *replacementId; + +/** + Optional. If specified, the transferee client waits for an invite to this room and joins it and then continues the transfer in this room. If absent, the transferee contacts the Matrix User ID given in the `targetUser` field in a room of its choosing. + */ +@property (nonatomic, nullable) NSString *targetRoomId; + +/** + An object giving information about the transfer target. + */ +@property (nonatomic, nullable) MXUserModel *targetUser; + +/** + If specified, gives the call ID for the transferee's client to use when placing the replacement call. Mutually exclusive with `awaitCallId`. + */ +@property (nonatomic, nullable) NSString *createCallId; + +/** + If specified, gives the call ID that the transferee's client should wait for. Mutually exclusive with `createCallId`. + */ +@property (nonatomic, nullable) NSString *awaitCallId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m new file mode 100644 index 0000000000..fc5dafa54d --- /dev/null +++ b/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m @@ -0,0 +1,38 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXCallReplacesEventContent.h" + +@implementation MXCallReplacesEventContent + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXCallReplacesEventContent *content = [[MXCallReplacesEventContent alloc] init]; + + if (content) + { + [content parseJSON:JSONDictionary]; + MXJSONModelSetString(content.replacementId, JSONDictionary[@"replacement_id"]); + MXJSONModelSetString(content.targetRoomId, JSONDictionary[@"target_room"]); + MXJSONModelSetMXJSONModel(content.targetUser, MXUserModel, JSONDictionary[@"target_user"]); + MXJSONModelSetString(content.createCallId, JSONDictionary[@"create_call"]); + MXJSONModelSetString(content.awaitCallId, JSONDictionary[@"await_call"]); + } + + return content; +} + +@end diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.h index a5215580ce..d62b110da3 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.h @@ -20,15 +20,10 @@ NS_ASSUME_NONNULL_BEGIN /** - `MXCallSelectAnswerEventContent` represents the content of a m.call.select_answer event. + `MXCallSelectAnswerEventContent` represents the content of an `m.call.select_answer` event. */ @interface MXCallSelectAnswerEventContent : MXCallEventContent -/** - A unique identifier for the call. - */ -@property (nonatomic) NSString *callId; - /** The selected party id. */ diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.m index 09c41d24fa..1a23dc7dbb 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallSelectAnswerEventContent.m @@ -24,7 +24,6 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary if (callSelectAnswerEventContent) { [callSelectAnswerEventContent parseJSON:JSONDictionary]; - MXJSONModelSetString(callSelectAnswerEventContent.callId, JSONDictionary[@"call_id"]); MXJSONModelSetString(callSelectAnswerEventContent.selectedPartyId, JSONDictionary[@"selected_party_id"]); } diff --git a/MatrixSDK/JSONModels/Call/Events/MXUserModel.h b/MatrixSDK/JSONModels/Call/Events/MXUserModel.h new file mode 100644 index 0000000000..a8afba7892 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/Events/MXUserModel.h @@ -0,0 +1,44 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXJSONModel.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + `MXUserModel` represents the target user of an `m.call.replaces` event. + @see MXCallReplacesEventContent + */ +@interface MXUserModel : MXJSONModel + +/** + The user id. + */ +@property (nonatomic) NSString *userId; + +/** + The user display name. + */ +@property (nonatomic, nullable) NSString *displayname; + +/** + The url of the user of the avatar. + */ +@property (nonatomic, nullable) NSString *avatarUrl; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/Call/Events/MXUserModel.m b/MatrixSDK/JSONModels/Call/Events/MXUserModel.m new file mode 100644 index 0000000000..530749a376 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/Events/MXUserModel.m @@ -0,0 +1,35 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXUserModel.h" + +@implementation MXUserModel + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXUserModel *userModel = [[MXUserModel alloc] init]; + + if (userModel) + { + MXJSONModelSetString(userModel.userId, JSONDictionary[@"id"]); + MXJSONModelSetString(userModel.displayname, JSONDictionary[@"display_name"]); + MXJSONModelSetString(userModel.avatarUrl, JSONDictionary[@"avatar_url"]); + } + + return userModel; +} + +@end diff --git a/MatrixSDK/JSONModels/Call/MXCallSessionDescription.h b/MatrixSDK/JSONModels/Call/MXCallSessionDescription.h index d3ab1acb41..7b53161d29 100644 --- a/MatrixSDK/JSONModels/Call/MXCallSessionDescription.h +++ b/MatrixSDK/JSONModels/Call/MXCallSessionDescription.h @@ -18,10 +18,10 @@ #import "MXJSONModel.h" typedef NSString * MXCallSessionDescriptionTypeString; -FOUNDATION_EXPORT NSString *const kMXCallSessionDescriptionTypeOffer; -FOUNDATION_EXPORT NSString *const kMXCallSessionDescriptionTypePrAnswer; -FOUNDATION_EXPORT NSString *const kMXCallSessionDescriptionTypeAnswer; -FOUNDATION_EXPORT NSString *const kMXCallSessionDescriptionTypeRollback; +FOUNDATION_EXPORT NSString *const kMXCallSessionDescriptionTypeStringOffer; +FOUNDATION_EXPORT NSString *const kMXCallSessionDescriptionTypeStringPrAnswer; +FOUNDATION_EXPORT NSString *const kMXCallSessionDescriptionTypeStringAnswer; +FOUNDATION_EXPORT NSString *const kMXCallSessionDescriptionTypeStringRollback; /** MXCallSessionDescription types diff --git a/MatrixSDK/JSONModels/Call/MXCallSessionDescription.m b/MatrixSDK/JSONModels/Call/MXCallSessionDescription.m index e89b7dadc0..8db64bb861 100644 --- a/MatrixSDK/JSONModels/Call/MXCallSessionDescription.m +++ b/MatrixSDK/JSONModels/Call/MXCallSessionDescription.m @@ -17,10 +17,10 @@ #import "MXCallSessionDescription.h" #import "MXTools.h" -NSString *const kMXCallSessionDescriptionTypeOffer = @"offer"; -NSString *const kMXCallSessionDescriptionTypePrAnswer = @"pranswer"; -NSString *const kMXCallSessionDescriptionTypeAnswer = @"answer"; -NSString *const kMXCallSessionDescriptionTypeRollback = @"rollback"; +NSString *const kMXCallSessionDescriptionTypeStringOffer = @"offer"; +NSString *const kMXCallSessionDescriptionTypeStringPrAnswer = @"pranswer"; +NSString *const kMXCallSessionDescriptionTypeStringAnswer = @"answer"; +NSString *const kMXCallSessionDescriptionTypeStringRollback = @"rollback"; @implementation MXCallSessionDescription diff --git a/MatrixSDK/JSONModels/MXEvent.h b/MatrixSDK/JSONModels/MXEvent.h index ad5a425be9..2213680d40 100644 --- a/MatrixSDK/JSONModels/MXEvent.h +++ b/MatrixSDK/JSONModels/MXEvent.h @@ -72,6 +72,8 @@ typedef NS_ENUM(NSInteger, MXEventType) MXEventTypeCallHangup, MXEventTypeCallReject, MXEventTypeCallNegotiate, + MXEventTypeCallReplaces, + MXEventTypeCallRejectReplacement, MXEventTypeSticker, MXEventTypeRoomTombStone, MXEventTypeKeyVerificationRequest, @@ -134,6 +136,8 @@ FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallSelectAnswer; FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallHangup; FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallReject; FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallNegotiate; +FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallReplaces; +FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallRejectReplacement; FOUNDATION_EXPORT NSString *const kMXEventTypeStringSticker; FOUNDATION_EXPORT NSString *const kMXEventTypeStringRoomTombStone; FOUNDATION_EXPORT NSString *const kMXEventTypeStringTaggedEvents; diff --git a/MatrixSDK/JSONModels/MXEvent.m b/MatrixSDK/JSONModels/MXEvent.m index 3e0f6e51aa..971387a0be 100644 --- a/MatrixSDK/JSONModels/MXEvent.m +++ b/MatrixSDK/JSONModels/MXEvent.m @@ -63,6 +63,8 @@ NSString *const kMXEventTypeStringCallHangup = @"m.call.hangup"; NSString *const kMXEventTypeStringCallReject = @"m.call.reject"; NSString *const kMXEventTypeStringCallNegotiate = @"m.call.negotiate"; +NSString *const kMXEventTypeStringCallReplaces = @"m.call.replaces"; +NSString *const kMXEventTypeStringCallRejectReplacement = @"m.call.reject_replacement"; NSString *const kMXEventTypeStringSticker = @"m.sticker"; NSString *const kMXEventTypeStringRoomTombStone = @"m.room.tombstone"; NSString *const kMXEventTypeStringKeyVerificationRequest= @"m.key.verification.request"; diff --git a/MatrixSDK/MatrixSDK.h b/MatrixSDK/MatrixSDK.h index 0debc74768..83e9affac6 100644 --- a/MatrixSDK/MatrixSDK.h +++ b/MatrixSDK/MatrixSDK.h @@ -100,3 +100,14 @@ FOUNDATION_EXPORT NSString *MatrixSDKVersion; #import "MXEventScan.h" #import "MXMediaScan.h" + +#import "MXBase64Tools.h" +#import "MXBaseProfiler.h" + +#import "MXCallInviteEventContent.h" +#import "MXCallAnswerEventContent.h" +#import "MXCallSelectAnswerEventContent.h" +#import "MXCallCandidatesEventContent.h" +#import "MXCallRejectEventContent.h" +#import "MXCallNegotiateEventContent.h" +#import "MXCallReplacesEventContent.h" diff --git a/MatrixSDK/Utils/MXTools.h b/MatrixSDK/Utils/MXTools.h index 013f4dddc5..ec131789ed 100644 --- a/MatrixSDK/Utils/MXTools.h +++ b/MatrixSDK/Utils/MXTools.h @@ -28,6 +28,7 @@ #import "MXEnumConstants.h" #import "MXCallHangupEventContent.h" #import "MXCallSessionDescription.h" +#import "MXCallRejectReplacementEventContent.h" @interface MXTools : NSObject @@ -46,6 +47,9 @@ + (MXCallSessionDescriptionType)callSessionDescriptionType:(MXCallSessionDescriptionTypeString)typeString; + (MXCallSessionDescriptionTypeString)callSessionDescriptionTypeString:(MXCallSessionDescriptionType)type; ++ (MXCallRejectReplacementReason)callRejectReplacementReason:(MXCallRejectReplacementReasonString)reasonString; ++ (MXCallRejectReplacementReasonString)callRejectReplacementReasonString:(MXCallRejectReplacementReason)reason; + /** Generate a random secret key. diff --git a/MatrixSDK/Utils/MXTools.m b/MatrixSDK/Utils/MXTools.m index 8ab605b639..77fe1079f3 100644 --- a/MatrixSDK/Utils/MXTools.m +++ b/MatrixSDK/Utils/MXTools.m @@ -283,27 +283,27 @@ + (MXCallHangupReason)callHangupReason:(MXCallHangupReasonString)reasonString { MXCallHangupReason reason = MXCallHangupReasonUserHangup; - if ([reasonString isEqualToString:kMXCallHangupReasonUserHangup]) + if ([reasonString isEqualToString:kMXCallHangupReasonStringUserHangup]) { reason = MXCallHangupReasonUserHangup; } - else if ([reasonString isEqualToString:kMXCallHangupReasonIceFailed]) + else if ([reasonString isEqualToString:kMXCallHangupReasonStringIceFailed]) { reason = MXCallHangupReasonIceFailed; } - else if ([reasonString isEqualToString:kMXCallHangupReasonInviteTimeout]) + else if ([reasonString isEqualToString:kMXCallHangupReasonStringInviteTimeout]) { reason = MXCallHangupReasonInviteTimeout; } - else if ([reasonString isEqualToString:kMXCallHangupReasonIceTimeout]) + else if ([reasonString isEqualToString:kMXCallHangupReasonStringIceTimeout]) { reason = MXCallHangupReasonIceTimeout; } - else if ([reasonString isEqualToString:kMXCallHangupReasonUserMediaFailed]) + else if ([reasonString isEqualToString:kMXCallHangupReasonStringUserMediaFailed]) { reason = MXCallHangupReasonUserMediaFailed; } - else if ([reasonString isEqualToString:kMXCallHangupReasonUnknownError]) + else if ([reasonString isEqualToString:kMXCallHangupReasonStringUnknownError]) { reason = MXCallHangupReasonUnknownError; } @@ -318,22 +318,22 @@ + (MXCallHangupReasonString)callHangupReasonString:(MXCallHangupReason)reason switch (reason) { case MXCallHangupReasonUserHangup: - string = kMXCallHangupReasonUserHangup; + string = kMXCallHangupReasonStringUserHangup; break; case MXCallHangupReasonIceFailed: - string = kMXCallHangupReasonIceFailed; + string = kMXCallHangupReasonStringIceFailed; break; case MXCallHangupReasonInviteTimeout: - string = kMXCallHangupReasonInviteTimeout; + string = kMXCallHangupReasonStringInviteTimeout; break; case MXCallHangupReasonIceTimeout: - string = kMXCallHangupReasonIceTimeout; + string = kMXCallHangupReasonStringIceTimeout; break; case MXCallHangupReasonUserMediaFailed: - string = kMXCallHangupReasonUserMediaFailed; + string = kMXCallHangupReasonStringUserMediaFailed; break; case MXCallHangupReasonUnknownError: - string = kMXCallHangupReasonUnknownError; + string = kMXCallHangupReasonStringUnknownError; break; default: break; @@ -346,19 +346,19 @@ + (MXCallSessionDescriptionType)callSessionDescriptionType:(MXCallSessionDescrip { MXCallSessionDescriptionType type = MXCallSessionDescriptionTypeOffer; - if ([typeString isEqualToString:kMXCallSessionDescriptionTypeOffer]) + if ([typeString isEqualToString:kMXCallSessionDescriptionTypeStringOffer]) { type = MXCallSessionDescriptionTypeOffer; } - else if ([typeString isEqualToString:kMXCallSessionDescriptionTypePrAnswer]) + else if ([typeString isEqualToString:kMXCallSessionDescriptionTypeStringPrAnswer]) { type = MXCallSessionDescriptionTypePrAnswer; } - else if ([typeString isEqualToString:kMXCallSessionDescriptionTypeAnswer]) + else if ([typeString isEqualToString:kMXCallSessionDescriptionTypeStringAnswer]) { type = MXCallSessionDescriptionTypeAnswer; } - else if ([typeString isEqualToString:kMXCallSessionDescriptionTypeRollback]) + else if ([typeString isEqualToString:kMXCallSessionDescriptionTypeStringRollback]) { type = MXCallSessionDescriptionTypeRollback; } @@ -373,16 +373,63 @@ + (MXCallSessionDescriptionTypeString)callSessionDescriptionTypeString:(MXCallSe switch (type) { case MXCallSessionDescriptionTypeOffer: - string = kMXCallSessionDescriptionTypeOffer; + string = kMXCallSessionDescriptionTypeStringOffer; break; case MXCallSessionDescriptionTypePrAnswer: - string = kMXCallSessionDescriptionTypePrAnswer; + string = kMXCallSessionDescriptionTypeStringPrAnswer; break; case MXCallSessionDescriptionTypeAnswer: - string = kMXCallSessionDescriptionTypeAnswer; + string = kMXCallSessionDescriptionTypeStringAnswer; break; case MXCallSessionDescriptionTypeRollback: - string = kMXCallSessionDescriptionTypeRollback; + string = kMXCallSessionDescriptionTypeStringRollback; + break; + } + + return string; +} + ++ (MXCallRejectReplacementReason)callRejectReplacementReason:(MXCallRejectReplacementReasonString)reasonString +{ + MXCallRejectReplacementReason type = MXCallRejectReplacementReasonDeclined; + + if ([reasonString isEqualToString:kMXCallRejectReplacementReasonStringDeclined]) + { + type = MXCallRejectReplacementReasonDeclined; + } + else if ([reasonString isEqualToString:kMXCallRejectReplacementReasonStringFailedRoomInvite]) + { + type = MXCallRejectReplacementReasonFailedRoomInvite; + } + else if ([reasonString isEqualToString:kMXCallRejectReplacementReasonStringFailedCallInvite]) + { + type = MXCallRejectReplacementReasonFailedCallInvite; + } + else if ([reasonString isEqualToString:kMXCallRejectReplacementReasonStringFailedCall]) + { + type = MXCallRejectReplacementReasonFailedCall; + } + + return type; +} + ++ (MXCallRejectReplacementReasonString)callRejectReplacementReasonString:(MXCallRejectReplacementReason)reason +{ + MXCallRejectReplacementReasonString string; + + switch (reason) + { + case MXCallRejectReplacementReasonDeclined: + string = kMXCallRejectReplacementReasonStringDeclined; + break; + case MXCallRejectReplacementReasonFailedRoomInvite: + string = kMXCallRejectReplacementReasonStringFailedRoomInvite; + break; + case MXCallRejectReplacementReasonFailedCallInvite: + string = kMXCallRejectReplacementReasonStringFailedCallInvite; + break; + case MXCallRejectReplacementReasonFailedCall: + string = kMXCallRejectReplacementReasonStringFailedCall; break; } diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 90b0e44a2d..f516c228ae 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -268,7 +268,7 @@ - (void)callWithVideo:(BOOL)video NSMutableDictionary *content = [@{ @"call_id": self.callId, @"offer": @{ - @"type": kMXCallSessionDescriptionTypeOffer, + @"type": kMXCallSessionDescriptionTypeStringOffer, @"sdp": sdp }, @"version": kMXCallVersion, @@ -339,7 +339,7 @@ - (void)answer NSDictionary *content = @{ @"call_id": self.callId, @"answer": @{ - @"type": kMXCallSessionDescriptionTypeAnswer, + @"type": kMXCallSessionDescriptionTypeStringAnswer, @"sdp": sdpAnswer }, @"version": kMXCallVersion, @@ -434,7 +434,7 @@ - (void)hold:(BOOL)hold NSMutableDictionary *content = [@{ @"call_id": self.callId, @"description": @{ - @"type": kMXCallSessionDescriptionTypeOffer, + @"type": kMXCallSessionDescriptionTypeStringOffer, @"sdp": sdp }, @"version": kMXCallVersion, @@ -1061,7 +1061,7 @@ - (void)handleCallNegotiate:(MXEvent *)event NSDictionary *content = @{ @"call_id": self.callId, @"description": @{ - @"type": kMXCallSessionDescriptionTypeAnswer, + @"type": kMXCallSessionDescriptionTypeStringAnswer, @"sdp": sdpAnswer }, @"version": kMXCallVersion, diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 1daa018175..3399582393 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -32,6 +32,8 @@ #import "MXCallCandidatesEventContent.h" #import "MXCallRejectEventContent.h" #import "MXCallNegotiateEventContent.h" +#import "MXCallReplacesEventContent.h" +#import "MXCallRejectReplacementEventContent.h" #pragma mark - Constants definitions NSString *const kMXCallManagerNewCall = @"kMXCallManagerNewCall"; @@ -86,7 +88,9 @@ - (instancetype)initWithMatrixSession:(MXSession *)mxSession andCallStack:(id Date: Fri, 11 Dec 2020 15:16:16 +0300 Subject: [PATCH 50/89] Introduce lifetime in replaces event --- .../JSONModels/Call/Events/MXCallReplacesEventContent.h | 6 ++++++ .../JSONModels/Call/Events/MXCallReplacesEventContent.m | 1 + 2 files changed, 7 insertions(+) diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.h index ba5eae9db8..0f7fe361ff 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.h @@ -29,6 +29,12 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic) NSString *replacementId; +/** + The time in milliseconds that the transfer request is valid for. + Once the request age exceeds this value, clients should discard it. + */ +@property (nonatomic) NSUInteger lifetime; + /** Optional. If specified, the transferee client waits for an invite to this room and joins it and then continues the transfer in this room. If absent, the transferee contacts the Matrix User ID given in the `targetUser` field in a room of its choosing. */ diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m index fc5dafa54d..d45e9507f6 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m @@ -26,6 +26,7 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary { [content parseJSON:JSONDictionary]; MXJSONModelSetString(content.replacementId, JSONDictionary[@"replacement_id"]); + MXJSONModelSetUInteger(content.lifetime, JSONDictionary[@"lifetime"]); MXJSONModelSetString(content.targetRoomId, JSONDictionary[@"target_room"]); MXJSONModelSetMXJSONModel(content.targetUser, MXUserModel, JSONDictionary[@"target_user"]); MXJSONModelSetString(content.createCallId, JSONDictionary[@"create_call"]); From a369e78c0f0b9669e0f07fc6a26e132905305875 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 11 Dec 2020 15:16:46 +0300 Subject: [PATCH 51/89] Introduce default lifetime for transfers --- MatrixSDK/VoIP/MXCallManager.h | 6 ++++++ MatrixSDK/VoIP/MXCallManager.m | 1 + 2 files changed, 7 insertions(+) diff --git a/MatrixSDK/VoIP/MXCallManager.h b/MatrixSDK/VoIP/MXCallManager.h index a53b8ae5b6..913414b2e6 100644 --- a/MatrixSDK/VoIP/MXCallManager.h +++ b/MatrixSDK/VoIP/MXCallManager.h @@ -144,6 +144,12 @@ extern NSString *const kMXCallManagerConferenceFinished; */ @property (nonatomic) NSUInteger negotiateLifetime; +/** + The time in milliseconds that an transfer call request is valid for. + Default is 30s. + */ +@property (nonatomic) NSUInteger transferLifetime; + /** The list of TURN/STUN servers advertised by the user's homeserver. Can be nil. In this case, use `fallbackSTUNServer`. diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 3399582393..48de43f512 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -77,6 +77,7 @@ - (instancetype)initWithMatrixSession:(MXSession *)mxSession andCallStack:(id Date: Fri, 11 Dec 2020 15:18:42 +0300 Subject: [PATCH 52/89] Fix comment --- MatrixSDK/VoIP/MXCallManager.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 48de43f512..aa7d7f81b0 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -494,7 +494,7 @@ - (void)handleCallNegotiate:(MXEvent *)event { MXCallNegotiateEventContent *content = [MXCallNegotiateEventContent modelFromJSON:event.content]; - // Check expiration (usefull filter when receiving load of events when resuming the event stream) + // Check expiration (useful filter when receiving load of events when resuming the event stream) if (event.age < content.lifetime) { if ([event.sender isEqualToString:_mxSession.myUserId] && From 5778f32c8b32abb3ab355516f487660fd0b5e76c Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 11 Dec 2020 15:20:02 +0300 Subject: [PATCH 53/89] Implement JSON methods --- .../Call/Events/MXCallEventContent.m | 19 ++++++++++++++ .../Call/Events/MXCallReplacesEventContent.m | 26 +++++++++++++++++++ .../JSONModels/Call/Events/MXUserModel.m | 16 ++++++++++++ 3 files changed, 61 insertions(+) diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m index b4c2238f43..26a4810afd 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m @@ -53,6 +53,25 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary return callEventContent; } +- (NSDictionary *)JSONDictionary +{ + NSMutableDictionary *jsonDictionary = [NSMutableDictionary dictionaryWithDictionary:@{ + @"call_id": self.callId, + @"party_id": self.partyId + }]; + + if (self.versionNumber) + { + jsonDictionary[@"version"] = self.versionNumber; + } + else if (self.versionString) + { + jsonDictionary[@"version"] = self.versionString; + } + + return jsonDictionary; +} + - (NSString *)version { NSString *_version; diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m index d45e9507f6..306fa6f937 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m @@ -36,4 +36,30 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary return content; } +- (NSDictionary *)JSONDictionary +{ + NSMutableDictionary *jsonDictionary = [super.JSONDictionary mutableCopy]; + + jsonDictionary[@"replacement_id"] = self.replacementId; + jsonDictionary[@"lifetime"] = @(self.lifetime); + if (self.targetRoomId) + { + jsonDictionary[@"target_room"] = self.targetRoomId; + } + if (self.targetUser) + { + jsonDictionary[@"target_user"] = self.targetUser.JSONDictionary; + } + if (self.createCallId) + { + jsonDictionary[@"create_call"] = self.createCallId; + } + if (self.awaitCallId) + { + jsonDictionary[@"await_call"] = self.awaitCallId; + } + + return jsonDictionary; +} + @end diff --git a/MatrixSDK/JSONModels/Call/Events/MXUserModel.m b/MatrixSDK/JSONModels/Call/Events/MXUserModel.m index 530749a376..6806c4b172 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXUserModel.m +++ b/MatrixSDK/JSONModels/Call/Events/MXUserModel.m @@ -32,4 +32,20 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary return userModel; } +- (NSDictionary *)JSONDictionary +{ + NSMutableDictionary *jsonDictionary = [NSMutableDictionary dictionaryWithObject:self.userId forKey:@"id"]; + + if (self.displayname) + { + jsonDictionary[@"display_name"] = self.displayname; + } + if (self.avatarUrl) + { + jsonDictionary[@"avatar_url"] = self.avatarUrl; + } + + return jsonDictionary; +} + @end From 05bcc61ac1547ea295b12fba72a1c6cc715ab2a3 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 11 Dec 2020 15:20:28 +0300 Subject: [PATCH 54/89] Specify some initializers for MXUserModel --- .../JSONModels/Call/Events/MXUserModel.h | 14 +++++++++++++ .../JSONModels/Call/Events/MXUserModel.m | 21 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/MatrixSDK/JSONModels/Call/Events/MXUserModel.h b/MatrixSDK/JSONModels/Call/Events/MXUserModel.h index a8afba7892..2092668316 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXUserModel.h +++ b/MatrixSDK/JSONModels/Call/Events/MXUserModel.h @@ -18,6 +18,8 @@ NS_ASSUME_NONNULL_BEGIN +@class MXUser; + /** `MXUserModel` represents the target user of an `m.call.replaces` event. @see MXCallReplacesEventContent @@ -39,6 +41,18 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, nullable) NSString *avatarUrl; +/** + Initialize model object with params. + */ +- (id)initWithUserId:(NSString * _Nonnull)userId + displayname:(NSString * _Nullable)displayname + avatarUrl:(NSString * _Nullable)avatarUrl; + +/** + Initialize model object with a user. + */ +- (id)initWithUser:(MXUser *)user; + @end NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/Call/Events/MXUserModel.m b/MatrixSDK/JSONModels/Call/Events/MXUserModel.m index 6806c4b172..6ac813f107 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXUserModel.m +++ b/MatrixSDK/JSONModels/Call/Events/MXUserModel.m @@ -15,6 +15,7 @@ // #import "MXUserModel.h" +#import "MXUser.h" @implementation MXUserModel @@ -48,4 +49,24 @@ - (NSDictionary *)JSONDictionary return jsonDictionary; } +- (id)initWithUserId:(NSString * _Nonnull)userId + displayname:(NSString * _Nullable)displayname + avatarUrl:(NSString * _Nullable)avatarUrl +{ + if (self = [super init]) + { + self.userId = userId; + self.displayname = displayname; + self.avatarUrl = avatarUrl; + } + return self; +} + +- (id)initWithUser:(MXUser *)user +{ + return [self initWithUserId:user.userId + displayname:user.displayname + avatarUrl:user.avatarUrl]; +} + @end From dbdc46bc9a4ff9c505ca0475612e1cec6780d835 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 11 Dec 2020 15:37:29 +0300 Subject: [PATCH 55/89] Introduce transfer method, move hold to its section --- MatrixSDK/VoIP/MXCall.h | 36 ++++++-- MatrixSDK/VoIP/MXCall.m | 187 ++++++++++++++++++++++++++++------------ 2 files changed, 162 insertions(+), 61 deletions(-) diff --git a/MatrixSDK/VoIP/MXCall.h b/MatrixSDK/VoIP/MXCall.h index fab07141ee..94a9d9f432 100644 --- a/MatrixSDK/VoIP/MXCall.h +++ b/MatrixSDK/VoIP/MXCall.h @@ -30,6 +30,7 @@ NS_ASSUME_NONNULL_BEGIN @class MXCallManager; @class MXEvent; @class MXRoom; +@class MXUserModel; /** Call states. @@ -128,6 +129,18 @@ extern NSString *const kMXCallSupportsHoldingStatusDidChange; */ - (void)answer; +/** + Hang up a call in progress or reject an incoming call. + */ +- (void)hangup; + +/** + Hang up a call with a reason in progress. + */ +- (void)hangupWithReason:(MXCallHangupReason)reason; + +#pragma mark - Hold + /** Flag to indicate that the call can be holded. */ @@ -144,15 +157,26 @@ extern NSString *const kMXCallSupportsHoldingStatusDidChange; */ @property (nonatomic, readonly) BOOL isOnHold; -/** - Hang up a call in progress or reject an incoming call. - */ -- (void)hangup; +#pragma mark - Transfer /** - Hang up a call with a reason in progress. + Flag to indicate that the call can be transferred. */ -- (void)hangupWithReason:(MXCallHangupReason)reason; +@property (nonatomic, readonly) BOOL supportsTransferring; + +/// Attempts to send an `m.call.replaces` event to the signaling room for this call. +/// @param targetRoomId Tells other party about the transfer target room. Optional. If specified, the transferee waits for an invite to this room and after join continues the transfer in this room. Otherwise, the transferee contacts the user given in the `targetUser` field in a room of its choosing. +/// @param targetUser Tells other party about the target user of the call transfer. Optional for the calls to the transfer target. +/// @param createCallId Tells other party to create a new call with this identifier. Mutually exclusive with `awaitCallId`. +/// @param awaitCallId Tells other party to wait for a call with this identifier. Mutually exclusive with `createCallId`. +/// @param success Success block. Returns event identifier for the event +/// @param failure Failure block. Returns error +- (void)transferToRoom:(NSString * _Nullable)targetRoomId + user:(MXUserModel * _Nullable)targetUser + createCall:(NSString * _Nullable)createCallId + awaitCall:(NSString * _Nullable)awaitCallId + success:(void (^)(NSString * _Nonnull eventId))success + failure:(void (^)(NSError * _Nullable error))failure; #pragma mark - Properties /** diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index f516c228ae..2ba7c2fa04 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -32,6 +32,9 @@ #import "MXCallCandidatesEventContent.h" #import "MXCallRejectEventContent.h" #import "MXCallNegotiateEventContent.h" +#import "MXCallReplacesEventContent.h" +#import "MXCallRejectReplacementEventContent.h" +#import "MXUserModel.h" #pragma mark - Constants definitions NSString *const kMXCallStateDidChange = @"kMXCallStateDidChange"; @@ -227,6 +230,12 @@ - (void)handleCallEvent:(MXEvent *)event case MXEventTypeCallNegotiate: [self handleCallNegotiate:event]; break; + case MXEventTypeCallReplaces: + [self handleCallReplaces:event]; + break; + case MXEventTypeCallRejectReplacement: + [self handleCallRejectReplacement:event]; + break; default: break; } @@ -384,6 +393,72 @@ - (void)answer } } +- (void)hangup +{ + NSLog(@"[MXCall] hangup"); + + if (self.state == MXCallStateRinging && [callInviteEventContent.version isEqualToString:kMXCallVersion]) + { + // Send the reject event for new call invites + NSDictionary *content = @{ + @"call_id": _callId, + @"version": kMXCallVersion, + @"party_id": self.partyId + }; + + [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallReject content:content localEcho:nil success:nil failure:^(NSError *error) { + NSLog(@"[MXCall] hangup: ERROR: Cannot send m.call.reject event."); + [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; + }]; + + // terminate with a fake reject event + MXEvent *fakeEvent = [MXEvent modelFromJSON:@{ + @"type": kMXEventTypeStringCallReject, + @"content": content + }]; + fakeEvent.sender = callManager.mxSession.myUserId; + [self terminateWithReason:fakeEvent]; + return; + } + + // hangup with the default reason + [self hangupWithReason:MXCallHangupReasonUserHangup]; +} + +- (void)hangupWithReason:(MXCallHangupReason)reason +{ + NSLog(@"[MXCall] hangupWithReason: %ld", (long)reason); + + if (self.state != MXCallStateEnded) + { + // Send the hangup event + NSDictionary *content = @{ + @"call_id": _callId, + @"version": kMXCallVersion, + @"party_id": self.partyId, + @"reason": [MXTools callHangupReasonString:reason] + }; + [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallHangup content:content localEcho:nil success:^(NSString *eventId) { + [[MXSDKOptions sharedInstance].analyticsDelegate trackValue:@(reason) + category:kMXAnalyticsVoipCategory + name:kMXAnalyticsVoipNameCallHangup]; + } failure:^(NSError *error) { + NSLog(@"[MXCall] hangupWithReason: ERROR: Cannot send m.call.hangup event."); + [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; + }]; + + // terminate with a fake hangup event + MXEvent *fakeEvent = [MXEvent modelFromJSON:@{ + @"type": kMXEventTypeStringCallHangup, + @"content": content + }]; + fakeEvent.sender = callManager.mxSession.myUserId; + [self terminateWithReason:fakeEvent]; + } +} + +#pragma mark - Hold + - (BOOL)supportsHolding { if (callInviteEventContent && _selectedAnswer && [callInviteEventContent.version isEqualToString:kMXCallVersion]) @@ -469,68 +544,60 @@ - (BOOL)isOnHold return _state == MXCallStateOnHold || _state == MXCallStateRemotelyOnHold; } -- (void)hangup -{ - NSLog(@"[MXCall] hangup"); - - if (self.state == MXCallStateRinging && [callInviteEventContent.version isEqualToString:kMXCallVersion]) - { - // Send the reject event for new call invites - NSDictionary *content = @{ - @"call_id": _callId, - @"version": kMXCallVersion, - @"party_id": self.partyId - }; - - [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallReject content:content localEcho:nil success:nil failure:^(NSError *error) { - NSLog(@"[MXCall] hangup: ERROR: Cannot send m.call.reject event."); - [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; - }]; - - // terminate with a fake reject event - MXEvent *fakeEvent = [MXEvent modelFromJSON:@{ - @"type": kMXEventTypeStringCallReject, - @"content": content - }]; - fakeEvent.sender = callManager.mxSession.myUserId; - [self terminateWithReason:fakeEvent]; - return; - } +#pragma mark - Transfer - // hangup with the default reason - [self hangupWithReason:MXCallHangupReasonUserHangup]; +- (BOOL)supportsTransferring +{ + // same conditions to support holding + return self.supportsHolding; } -- (void)hangupWithReason:(MXCallHangupReason)reason +- (void)transferToRoom:(NSString * _Nullable)targetRoomId + user:(MXUserModel * _Nullable)targetUser + createCall:(NSString * _Nullable)createCallId + awaitCall:(NSString * _Nullable)awaitCallId + success:(void (^)(NSString * _Nonnull eventId))success + failure:(void (^)(NSError * _Nullable error))failure { - NSLog(@"[MXCall] hangupWithReason: %ld", (long)reason); + MXCallReplacesEventContent *content = [[MXCallReplacesEventContent alloc] init]; - if (self.state != MXCallStateEnded) + // base fields + content.callId = self.callId; + content.versionString = kMXCallVersion; + content.partyId = self.partyId; + + // other fields + content.replacementId = [[NSUUID UUID] UUIDString]; + content.lifetime = self->callManager.transferLifetime; + + if (targetRoomId) { - // Send the hangup event - NSDictionary *content = @{ - @"call_id": _callId, - @"version": kMXCallVersion, - @"party_id": self.partyId, - @"reason": [MXTools callHangupReasonString:reason] - }; - [_callSignalingRoom sendEventOfType:kMXEventTypeStringCallHangup content:content localEcho:nil success:^(NSString *eventId) { - [[MXSDKOptions sharedInstance].analyticsDelegate trackValue:@(reason) - category:kMXAnalyticsVoipCategory - name:kMXAnalyticsVoipNameCallHangup]; - } failure:^(NSError *error) { - NSLog(@"[MXCall] hangupWithReason: ERROR: Cannot send m.call.hangup event."); - [self didEncounterError:error reason:MXCallHangupReasonUnknownError]; - }]; - - // terminate with a fake hangup event - MXEvent *fakeEvent = [MXEvent modelFromJSON:@{ - @"type": kMXEventTypeStringCallHangup, - @"content": content - }]; - fakeEvent.sender = callManager.mxSession.myUserId; - [self terminateWithReason:fakeEvent]; + content.targetRoomId = targetRoomId; + } + if (targetUser) + { + content.targetUser = targetUser; + } + if (createCallId) + { + content.createCallId = createCallId; } + if (awaitCallId) + { + content.awaitCallId = awaitCallId; + } + + [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallReplaces + content:content.JSONDictionary + localEcho:nil + success:success + failure:^(NSError *error) { + NSLog(@"[MXCall] transferToRoom: ERROR: Cannot send m.call.replaces event."); + if (failure) + { + failure(error); + } + }]; } #pragma mark - Properties @@ -1093,6 +1160,16 @@ - (void)handleCallNegotiate:(MXEvent *)event } } +- (void)handleCallReplaces:(MXEvent *)event +{ + // TODO: Implement +} + +- (void)handleCallRejectReplacement:(MXEvent *)event +{ + // TODO: Implement +} + #pragma mark - Private methods - (BOOL)canHandleNegotiationEvent:(MXEvent *)event From c7d155e9399a0824c78b385a04f73ec3504ace8e Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 11 Dec 2020 18:08:56 +0300 Subject: [PATCH 56/89] First version of transfer initiation --- MatrixSDK/VoIP/MXCallManager.h | 16 ++ MatrixSDK/VoIP/MXCallManager.m | 344 ++++++++++++++++++++++++++++++++- 2 files changed, 358 insertions(+), 2 deletions(-) diff --git a/MatrixSDK/VoIP/MXCallManager.h b/MatrixSDK/VoIP/MXCallManager.h index 913414b2e6..6ed9bf0ca8 100644 --- a/MatrixSDK/VoIP/MXCallManager.h +++ b/MatrixSDK/VoIP/MXCallManager.h @@ -26,6 +26,7 @@ NS_ASSUME_NONNULL_BEGIN @class MXSession; @class MXTurnServerResponse; @class MXEvent; +@class MXUserModel; @protocol MXCallStack; @@ -161,6 +162,21 @@ extern NSString *const kMXCallManagerConferenceFinished; */ @property (nonatomic) NSString *fallbackSTUNServer; +#pragma mark - Transfer + +/// Attempts to transfer the given call to a new call between the transferee and the target +/// @param callWithTransferee Call to be transferred +/// @param target Target user for the transfer +/// @param transferee Transferee user of the transfer +/// @param consultFirst Flag to indicate if we want to consult the transfer to the target user first. If set, creates a DM call to the target (if we don't have already one). Even would create a new DM if we don't have one already to call the target. +/// @param success Success block. Returns the new call id +/// @param failure Failure block +- (void)transferCall:(MXCall *)callWithTransferee + to:(MXUserModel *)target + withTransferee:(MXUserModel *)transferee + consultFirst:(BOOL)consultFirst + success:(void (^)(NSString * _Nonnull newCallId))success + failure:(void (^)(NSError * _Nullable error))failure; #pragma mark - Conference call diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index aa7d7f81b0..6b82477835 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -514,12 +514,36 @@ - (void)handleCallNegotiate:(MXEvent *)event - (void)handleCallReplaces:(MXEvent *)event { -// MXCallReplacesEventContent *content = [MXCallReplacesEventContent modelFromJSON:event.content]; + MXCallReplacesEventContent *content = [MXCallReplacesEventContent modelFromJSON:event.content]; + + // Check expiration (useful filter when receiving load of events when resuming the event stream) + if (event.age < content.lifetime) + { + if ([event.sender isEqualToString:_mxSession.myUserId] && + [content.partyId isEqualToString:_mxSession.myDeviceId]) + { + // this is a remote echo, ignore + return; + } + + MXCall *call = [self callWithCallId:content.callId]; + if (call) + { + [call handleCallEvent:event]; + } + } } - (void)handleCallRejectReplacement:(MXEvent *)event { -// MXCallRejectReplacementEventContent *content = [MXCallRejectReplacementEventContent modelFromJSON:event.content]; + MXCallRejectReplacementEventContent *content = [MXCallRejectReplacementEventContent modelFromJSON:event.content]; + + // Forward the event to the MXCall object + MXCall *call = [self callWithCallId:content.callId]; + if (call) + { + [call handleCallEvent:event]; + } } - (void)handleCallStateDidChangeNotification:(NSNotification *)notification @@ -579,6 +603,322 @@ - (void)unregisterFromNotifications } } +#pragma mark - Transfer + +- (void)transferCall:(MXCall *)callWithTransferee + to:(MXUserModel *)target + withTransferee:(MXUserModel *)transferee + consultFirst:(BOOL)consultFirst + success:(void (^)(NSString * _Nonnull newCallId))success + failure:(void (^)(NSError * _Nullable error))failure +{ + if (callWithTransferee.isConferenceCall) + { + // it's not intended to transfer conference calls + if (failure) + { + failure(nil); + } + return; + } + + MXWeakify(self); + + // find the call with target + + [self callWithUser:target.userId completion:^(MXCall * _Nullable call) { + + MXStrongifyAndReturnIfNil(self); + + // define continue block + void(^continueBlock)(MXCall *) = ^(MXCall *callWithTarget) { + + // find a suitable room (which only consists three users: self, the transferee and the target) + + MXWeakify(self); + + [self callTransferRoomWithUsers:@[target.userId, transferee.userId] completion:^(MXRoom * _Nullable transferRoom, BOOL isNewRoom) { + + MXStrongifyAndReturnIfNil(self); + + if (!transferRoom) + { + // A room cannot be found/created + if (failure) + { + failure(nil); + } + return; + } + + // generate a new call id + NSString *newCallId = [[NSUUID UUID] UUIDString]; + + // first, send replaces event to the call with the transferee, send info about target + // to make transferee start a new call + [callWithTransferee transferToRoom:transferRoom.roomId + user:target + createCall:newCallId + awaitCall:nil + success:^(NSString * _Nonnull eventId) { + + // define block to be called after replaces events are sent + void(^afterReplacesEventsBlock)(void) = ^{ + if (isNewRoom) + { + // if was a newly created room, send invites after replaces events + [transferRoom inviteUser:target.userId success:nil failure:failure]; + [transferRoom inviteUser:transferee.userId success:nil failure:failure]; + } + + // do not end the calls, wait for other parties to end it + + // TODO: Observe the call with the newCallId + + if (success) + { + success(newCallId); + } + }; + + if (callWithTarget) + { + // then, send replaces event to the call with the target, send info about transferee + // to make target await a call + [callWithTarget transferToRoom:transferRoom.roomId + user:transferee + createCall:nil + awaitCall:newCallId + success:^(NSString * _Nonnull eventId) { + + if (consultFirst) + { + // hold the callWithTransferee + [callWithTransferee hold:YES]; + + // consult with the target + [callWithTarget hold:NO]; + + // TODO: Make a state change on callWithTransferee here, like `onHoldForConsulting` and provide target details, to complete the transfer after + } + else + { + // no need to consult + + afterReplacesEventsBlock(); + } + + } failure:failure]; + } + else + { + // being here means no need to consult, otherwise would fail before + + afterReplacesEventsBlock(); + } + + } failure:failure]; + }]; + }; + + if (call) + { + continueBlock(call); + } + else + { + // we're not in a call with target + + if (consultFirst) + { + MXWeakify(self); + + [self directCallableRoomWithUser:target.userId completion:^(MXRoom * _Nullable room, NSError * _Nullable error) { + + MXStrongifyAndReturnIfNil(self); + + if (room == nil) + { + // could not find/create a direct room with target + if (failure) + { + failure(nil); + } + return; + } + + MXWeakify(self); + + // hold the transferee call before starting the new call + [callWithTransferee hold:YES]; + + // place a new call to the target + [self placeCallInRoom:room.roomId withVideo:callWithTransferee.isVideoCall success:^(MXCall * _Nonnull call) { + + MXStrongifyAndReturnIfNil(self); + + continueBlock(call); + } failure:^(NSError * _Nullable error) { + NSLog(@"[MXCallManager] transferCall: couldn't call the target: %@", error); + }]; + + }]; + } + else + { + // we don't need to consult, so we can continue without an active call with the target + continueBlock(nil); + } + } + }]; +} + +/// Attempts to find a room with the given users only. If not found, tries to create. If fails, completion will be called with a nil room. +/// @param userIds User IDs array to look for +/// @param completion Completion block +- (void)callTransferRoomWithUsers:(NSArray *)userIds + completion:(void (^ _Nonnull)(MXRoom * _Nullable room, BOOL isNewRoom))completion +{ + __block MXRoom *resultRoom = nil; + + dispatch_group_t roomGroup = dispatch_group_create(); + + for (MXRoom *room in self.mxSession.rooms) + { + dispatch_group_enter(roomGroup); + + [room state:^(MXRoomState *roomState) { + + NSArray *roomUserIds = [roomState.members.joinedMembers valueForKey:@"userId"]; + if (roomState.membersCount.joined == 3) // ignore other rooms (which might have these users but some extra ones too) + { + NSSet *roomUserIdSet = [NSSet setWithArray:roomUserIds]; + NSSet *desiredUserIdSet = [NSSet setWithArray:userIds]; + if ([desiredUserIdSet isSubsetOfSet:roomUserIdSet]) // if all userIds exist in roomUserIds + { + resultRoom = room; + } + } + + dispatch_group_leave(roomGroup); + }]; + } + + dispatch_group_notify(roomGroup, dispatch_get_main_queue(), ^{ + if (resultRoom) + { + completion(resultRoom, NO); + } + else + { + // no room found, create a new one + + [self.mxSession canEnableE2EByDefaultInNewRoomWithUsers:userIds success:^(BOOL canEnableE2E) { + + MXRoomCreationParameters *roomCreationParameters = [MXRoomCreationParameters new]; + roomCreationParameters.visibility = kMXRoomDirectoryVisibilityPrivate; + roomCreationParameters.preset = kMXRoomPresetTrustedPrivateChat; + roomCreationParameters.inviteArray = nil; // intentionally do not invite yet users + + if (canEnableE2E) + { + roomCreationParameters.initialStateEvents = @[ + [MXRoomCreationParameters initialStateEventForEncryptionWithAlgorithm:kMXCryptoMegolmAlgorithm + ]]; + } + + [self.mxSession createRoomWithParameters:roomCreationParameters success:^(MXRoom *room) { + completion(room, YES); + } failure:^(NSError *error) { + completion(nil, NO); + }]; + + } failure:^(NSError *error) { + completion(nil, NO); + }]; + } + }); +} + +/// Tries to find a direct & callable room with the given user. If not such a room found, tries to create it and then waits for the other party to join. +/// @param userId The user id to check. +/// @param completion Completion block. +- (void)directCallableRoomWithUser:(NSString * _Nonnull)userId + completion:(void (^_Nonnull)(MXRoom* _Nullable room, NSError * _Nullable error))completion +{ + MXRoom *room = [self.mxSession directJoinedRoomWithUserId:userId]; + if (room) + { + // TODO: Having a room probably does not mean it's callable (both party joined). Fix this. + completion(room, nil); + } + else + { + // we're not in a direct room with target, create it + [self.mxSession canEnableE2EByDefaultInNewRoomWithUsers:@[userId] success:^(BOOL canEnableE2E) { + + MXRoomCreationParameters *roomCreationParameters = [MXRoomCreationParameters parametersForDirectRoomWithUser:userId]; + roomCreationParameters.visibility = kMXRoomDirectoryVisibilityPrivate; + + if (canEnableE2E) + { + roomCreationParameters.initialStateEvents = @[ + [MXRoomCreationParameters initialStateEventForEncryptionWithAlgorithm:kMXCryptoMegolmAlgorithm + ]]; + } + + [self.mxSession createRoomWithParameters:roomCreationParameters success:^(MXRoom *room) { + // TODO: Wait for other party to join before returning the room + + completion(room, nil); + } failure:^(NSError *error) { + completion(nil, error); + }]; + + } failure:^(NSError *error) { + completion(nil, error); + }]; + } +} + +/// Tries to find the call to a given user. If fails, completion will be called with a nil call. +/// @param userId The user id to check. +/// @param completion Completion block. +- (void)callWithUser:(NSString * _Nonnull)userId + completion:(void (^_Nonnull)(MXCall* _Nullable call))completion +{ + __block MXCall *resultCall = nil; + + dispatch_group_t callGroup = dispatch_group_create(); + + for (MXCall *call in calls) + { + if (call.isConferenceCall) + { + continue; + } + if ([call.callerId isEqualToString:userId]) + { + resultCall = call; + break; + } + + dispatch_group_enter(callGroup); + + [call calleeId:^(NSString * _Nonnull calleeId) { + if ([calleeId isEqualToString:userId]) + { + resultCall = call; + } + dispatch_group_leave(callGroup); + }]; + } + + dispatch_group_notify(callGroup, dispatch_get_main_queue(), ^{ + completion(resultCall); + }); +} + #pragma mark - Conference call // Copied from vector-web: From 74c3a1ffd70105d2295ed6eb4ec8d78fc7b6ca11 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 11 Dec 2020 18:10:03 +0300 Subject: [PATCH 57/89] Update CHANGES.rst --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 0a730557d8..40cbc9cebf 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,6 +9,7 @@ Changes to be released in next version * Send VoIP analytics events (vector-im/element-ios/issues/3855). * Add hold support for CallKit calls (vector-im/element-ios/issues/3834). * Fix video call with web (vector-im/element-ios/issues/3862). + * VoIP: Call transfers initiation (vector-im/element-ios/issues/3872). 🐛 Bugfix * From 7ae5b52ed7e0c3d960a394d9e7facc5d622af510 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 15 Dec 2020 14:53:20 +0300 Subject: [PATCH 58/89] Add capabilities on call invites and answers --- MatrixSDK.xcodeproj/project.pbxproj | 12 +++++++ .../Call/Events/MXCallAnswerEventContent.h | 9 ++++- .../Call/Events/MXCallAnswerEventContent.m | 3 ++ .../Call/Events/MXCallCapabilitiesModel.h | 33 +++++++++++++++++++ .../Call/Events/MXCallCapabilitiesModel.m | 33 +++++++++++++++++++ .../Call/Events/MXCallInviteEventContent.h | 9 ++++- .../Call/Events/MXCallInviteEventContent.m | 3 ++ .../Call/Events/MXCallNegotiateEventContent.h | 3 +- .../Call/Events/MXCallNegotiateEventContent.m | 1 + .../Call/Events/MXCallReplacesEventContent.h | 3 +- .../Call/Events/MXCallReplacesEventContent.m | 1 + MatrixSDK/MatrixSDK.h | 2 ++ MatrixSDK/VoIP/MXCall.h | 14 +++++++- MatrixSDK/VoIP/MXCall.m | 22 +++++++++++-- MatrixSDK/VoIP/MXCallManager.m | 9 ++--- 15 files changed, 142 insertions(+), 15 deletions(-) create mode 100644 MatrixSDK/JSONModels/Call/Events/MXCallCapabilitiesModel.h create mode 100644 MatrixSDK/JSONModels/Call/Events/MXCallCapabilitiesModel.m diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 6e02fb4126..03e47ee24f 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -1230,6 +1230,10 @@ ECE36F1425823C8400122124 /* MXUserModel.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE36F1125823C8400122124 /* MXUserModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; ECE36F1525823C8400122124 /* MXUserModel.m in Sources */ = {isa = PBXBuildFile; fileRef = ECE36F1225823C8400122124 /* MXUserModel.m */; }; ECE36F1625823C8400122124 /* MXUserModel.m in Sources */ = {isa = PBXBuildFile; fileRef = ECE36F1225823C8400122124 /* MXUserModel.m */; }; + ECE36F3D2588D3B200122124 /* MXCallCapabilitiesModel.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE36F3B2588D3B200122124 /* MXCallCapabilitiesModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ECE36F3E2588D3B200122124 /* MXCallCapabilitiesModel.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE36F3B2588D3B200122124 /* MXCallCapabilitiesModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ECE36F3F2588D3B200122124 /* MXCallCapabilitiesModel.m in Sources */ = {isa = PBXBuildFile; fileRef = ECE36F3C2588D3B200122124 /* MXCallCapabilitiesModel.m */; }; + ECE36F402588D3B200122124 /* MXCallCapabilitiesModel.m in Sources */ = {isa = PBXBuildFile; fileRef = ECE36F3C2588D3B200122124 /* MXCallCapabilitiesModel.m */; }; ECFFA970255ED32700706454 /* MXCallNegotiateEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = ECFFA96F255ED32700706454 /* MXCallNegotiateEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; ECFFA971255ED32700706454 /* MXCallNegotiateEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = ECFFA96F255ED32700706454 /* MXCallNegotiateEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; ECFFA977255ED34500706454 /* MXCallNegotiateEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = ECFFA976255ED34500706454 /* MXCallNegotiateEventContent.m */; }; @@ -1929,6 +1933,8 @@ ECE36F0825822CA900122124 /* MXCallRejectReplacementEventContent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCallRejectReplacementEventContent.m; sourceTree = ""; }; ECE36F1125823C8400122124 /* MXUserModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXUserModel.h; sourceTree = ""; }; ECE36F1225823C8400122124 /* MXUserModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXUserModel.m; sourceTree = ""; }; + ECE36F3B2588D3B200122124 /* MXCallCapabilitiesModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCallCapabilitiesModel.h; sourceTree = ""; }; + ECE36F3C2588D3B200122124 /* MXCallCapabilitiesModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCallCapabilitiesModel.m; sourceTree = ""; }; ECFFA96F255ED32700706454 /* MXCallNegotiateEventContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCallNegotiateEventContent.h; sourceTree = ""; }; ECFFA976255ED34500706454 /* MXCallNegotiateEventContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCallNegotiateEventContent.m; sourceTree = ""; }; ED2F344856EFFCA383E37B22 /* Pods-SDK-MatrixSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDK-MatrixSDK.release.xcconfig"; path = "Target Support Files/Pods-SDK-MatrixSDK/Pods-SDK-MatrixSDK.release.xcconfig"; sourceTree = ""; }; @@ -3394,6 +3400,8 @@ ECE36F1225823C8400122124 /* MXUserModel.m */, ECE36F0725822CA900122124 /* MXCallRejectReplacementEventContent.h */, ECE36F0825822CA900122124 /* MXCallRejectReplacementEventContent.m */, + ECE36F3B2588D3B200122124 /* MXCallCapabilitiesModel.h */, + ECE36F3C2588D3B200122124 /* MXCallCapabilitiesModel.m */, ); path = Events; sourceTree = ""; @@ -3568,6 +3576,7 @@ B19A30BC2404268600FB6F35 /* MXQRCodeDataCoder.h in Headers */, 325D1C261DFECE0D0070B8BF /* MXCrypto_Private.h in Headers */, B10AFB4322A970060092E6AF /* MXEventReplace.h in Headers */, + ECE36F3D2588D3B200122124 /* MXCallCapabilitiesModel.h in Headers */, 327A5F4D239805F600ED6329 /* MXKeyVerificationStart.h in Headers */, B124BBCC256453C90028996D /* MXMembershipTransitionState.h in Headers */, 324DD2BF246D658500377005 /* MXHkdfSha256.h in Headers */, @@ -3978,6 +3987,7 @@ B14EF34F2397E90400758AF0 /* MXEventAnnotation.h in Headers */, B14EF3502397E90400758AF0 /* MXIdentityService.h in Headers */, B14EF3512397E90400758AF0 /* MXCallKitAdapter.h in Headers */, + ECE36F3E2588D3B200122124 /* MXCallCapabilitiesModel.h in Headers */, B14EF3522397E90400758AF0 /* MXSASTransaction.h in Headers */, B14EF3532397E90400758AF0 /* MXNotificationCenter.h in Headers */, B14EF3542397E90400758AF0 /* MXLoginPolicy.h in Headers */, @@ -4420,6 +4430,7 @@ 32BBAE6C2178E99100D85F46 /* MXKeyBackupData.m in Sources */, 32AF928C240EA3880008A0FD /* MXSecretShareSend.m in Sources */, 324DD29B246AD2B500377005 /* MXSecretStorage.m in Sources */, + ECE36F3F2588D3B200122124 /* MXCallCapabilitiesModel.m in Sources */, 3281E8BA19E42DFE00976E1A /* MXJSONModels.m in Sources */, 3245A7531AF7B2930001D8A7 /* MXCallManager.m in Sources */, 32E226A71D06AC9F00E6CA54 /* MXPeekingRoom.m in Sources */, @@ -4608,6 +4619,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + ECE36F402588D3B200122124 /* MXCallCapabilitiesModel.m in Sources */, B14EF1CA2397E90400758AF0 /* MXOlmInboundGroupSession.m in Sources */, B14EF1CB2397E90400758AF0 /* MXRoomAccountData.m in Sources */, B14EF1CC2397E90400758AF0 /* MXEventAnnotationChunk.m in Sources */, diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.h index 2428752578..06027b2af7 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.h @@ -16,10 +16,12 @@ #import #import "MXCallEventContent.h" -#import "MXCallSessionDescription.h" NS_ASSUME_NONNULL_BEGIN +@class MXCallSessionDescription; +@class MXCallCapabilitiesModel; + /** `MXCallAnswerEventContent` represents the content of an `m.call.answer` event. */ @@ -30,6 +32,11 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic) MXCallSessionDescription *answer; +/** + Capabilities for this call. + */ +@property (nonatomic, nullable) MXCallCapabilitiesModel *capabilities; + @end NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.m index 67127dc8db..3755e675a6 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallAnswerEventContent.m @@ -15,6 +15,8 @@ // #import "MXCallAnswerEventContent.h" +#import "MXCallSessionDescription.h" +#import "MXCallCapabilitiesModel.h" @implementation MXCallAnswerEventContent @@ -25,6 +27,7 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary { [callAnswerEventContent parseJSON:JSONDictionary]; MXJSONModelSetMXJSONModel(callAnswerEventContent.answer, MXCallSessionDescription, JSONDictionary[@"answer"]); + MXJSONModelSetMXJSONModel(callAnswerEventContent.capabilities, MXCallCapabilitiesModel, JSONDictionary[@"capabilities"]); } return callAnswerEventContent; diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallCapabilitiesModel.h b/MatrixSDK/JSONModels/Call/Events/MXCallCapabilitiesModel.h new file mode 100644 index 0000000000..4c64462506 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/Events/MXCallCapabilitiesModel.h @@ -0,0 +1,33 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + `MXCallCapabilitiesModel` represents capabilities in `m.call.invite` and `m.call.answer` events. + */ +@interface MXCallCapabilitiesModel : MXJSONModel + +/** + Capability defined by `m.call.transferee`. + */ +@property (nonatomic) BOOL transferee; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallCapabilitiesModel.m b/MatrixSDK/JSONModels/Call/Events/MXCallCapabilitiesModel.m new file mode 100644 index 0000000000..24d4535a12 --- /dev/null +++ b/MatrixSDK/JSONModels/Call/Events/MXCallCapabilitiesModel.m @@ -0,0 +1,33 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXCallCapabilitiesModel.h" + +@implementation MXCallCapabilitiesModel + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXCallCapabilitiesModel *capabilitiesModel = [[MXCallCapabilitiesModel alloc] init]; + + if (capabilitiesModel) + { + MXJSONModelSetBoolean(capabilitiesModel.transferee, JSONDictionary[@"m.call.transferee"]); + } + + return capabilitiesModel; +} + +@end diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.h index f325ebb83f..39df89272b 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.h @@ -16,10 +16,12 @@ #import #import "MXCallEventContent.h" -#import "MXCallSessionDescription.h" NS_ASSUME_NONNULL_BEGIN +@class MXCallSessionDescription; +@class MXCallCapabilitiesModel; + /** `MXCallInviteEventContent` represents the content of an `m.call.invite` event. */ @@ -42,6 +44,11 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, copy, nullable) NSString *invitee; +/** + Capabilities for this call. + */ +@property (nonatomic) MXCallCapabilitiesModel *capabilities; + /** Indicate whether the invitation is for a video call. */ diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.m index 3a0bafe1eb..c612683216 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallInviteEventContent.m @@ -15,6 +15,8 @@ // #import "MXCallInviteEventContent.h" +#import "MXCallSessionDescription.h" +#import "MXCallCapabilitiesModel.h" @implementation MXCallInviteEventContent @@ -27,6 +29,7 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary MXJSONModelSetMXJSONModel(callInviteEventContent.offer, MXCallSessionDescription, JSONDictionary[@"offer"]); MXJSONModelSetUInteger(callInviteEventContent.lifetime, JSONDictionary[@"lifetime"]); MXJSONModelSetString(callInviteEventContent.invitee, JSONDictionary[@"invitee"]); + MXJSONModelSetMXJSONModel(callInviteEventContent.capabilities, MXCallCapabilitiesModel, JSONDictionary[@"capabilities"]); } return callInviteEventContent; diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h index 48a0019601..a93d6ffc38 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h @@ -16,10 +16,11 @@ #import #import "MXCallEventContent.h" -#import "MXCallSessionDescription.h" NS_ASSUME_NONNULL_BEGIN +@class MXCallSessionDescription; + /** `MXCallNegotiateEventContent` represents the content of an `m.call.negotiate` event. */ diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m index a2bc1edb0e..d91b7e533c 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.m @@ -15,6 +15,7 @@ // #import "MXCallNegotiateEventContent.h" +#import "MXCallSessionDescription.h" @implementation MXCallNegotiateEventContent diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.h index 0f7fe361ff..59fd125656 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.h @@ -15,10 +15,11 @@ // #import "MXCallEventContent.h" -#import "MXUserModel.h" NS_ASSUME_NONNULL_BEGIN +@class MXUserModel; + /** `MXCallReplacesEventContent` represents the content of an `m.call.replaces` event. */ diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m index 306fa6f937..40b66b8804 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallReplacesEventContent.m @@ -15,6 +15,7 @@ // #import "MXCallReplacesEventContent.h" +#import "MXUserModel.h" @implementation MXCallReplacesEventContent diff --git a/MatrixSDK/MatrixSDK.h b/MatrixSDK/MatrixSDK.h index 83e9affac6..d9b71426a1 100644 --- a/MatrixSDK/MatrixSDK.h +++ b/MatrixSDK/MatrixSDK.h @@ -111,3 +111,5 @@ FOUNDATION_EXPORT NSString *MatrixSDKVersion; #import "MXCallRejectEventContent.h" #import "MXCallNegotiateEventContent.h" #import "MXCallReplacesEventContent.h" +#import "MXUserModel.h" +#import "MXCallCapabilitiesModel.h" diff --git a/MatrixSDK/VoIP/MXCall.h b/MatrixSDK/VoIP/MXCall.h index 94a9d9f432..418e9e6a24 100644 --- a/MatrixSDK/VoIP/MXCall.h +++ b/MatrixSDK/VoIP/MXCall.h @@ -82,6 +82,12 @@ extern NSString *const kMXCallStateDidChange; */ extern NSString *const kMXCallSupportsHoldingStatusDidChange; +/** + Posted when a `MXCall` object has changed its status to support transferring. + The notification object is the `MXKCall` object representing the call. + */ +extern NSString *const kMXCallSupportsTransferringStatusDidChange; + @protocol MXCallDelegate; /** @@ -327,10 +333,16 @@ extern NSString *const kMXCallSupportsHoldingStatusDidChange; /** Tells the delegate that status of the call to support holding has changed. - @param call the instance that changes, + @param call the instance that changes */ - (void)callSupportsHoldingStatusDidChange:(MXCall *)call; +/** + Tells the delegate that status of the call to support transferring has changed. + @param call the instance that changes + */ +- (void)callSupportsTransferringStatusDidChange:(MXCall *)call; + /** Tells the delegate an error occured. The call cannot be established. diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 2ba7c2fa04..fdf3811e3c 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -35,10 +35,12 @@ #import "MXCallReplacesEventContent.h" #import "MXCallRejectReplacementEventContent.h" #import "MXUserModel.h" +#import "MXCallCapabilitiesModel.h" #pragma mark - Constants definitions NSString *const kMXCallStateDidChange = @"kMXCallStateDidChange"; NSString *const kMXCallSupportsHoldingStatusDidChange = @"kMXCallSupportsHoldingStatusDidChange"; +NSString *const kMXCallSupportsTransferringStatusDidChange = @"kMXCallSupportsTransferringStatusDidChange"; @interface MXCall () { @@ -548,8 +550,12 @@ - (BOOL)isOnHold - (BOOL)supportsTransferring { - // same conditions to support holding - return self.supportsHolding; + if (callInviteEventContent && _selectedAnswer) + { + MXCallAnswerEventContent *content = [MXCallAnswerEventContent modelFromJSON:_selectedAnswer.content]; + return callInviteEventContent.capabilities.transferee && content.capabilities.transferee; + } + return NO; } - (void)transferToRoom:(NSString * _Nullable)targetRoomId @@ -617,10 +623,20 @@ - (void)setSelectedAnswer:(MXEvent *)selectedAnswer [_delegate callSupportsHoldingStatusDidChange:self]; } - // Broadcast the new call status + if ([_delegate respondsToSelector:@selector(callSupportsTransferringStatusDidChange:)]) + { + [_delegate callSupportsTransferringStatusDidChange:self]; + } + + // Broadcast the new call statuses + [[NSNotificationCenter defaultCenter] postNotificationName:kMXCallSupportsHoldingStatusDidChange object:self userInfo:nil]; + + [[NSNotificationCenter defaultCenter] postNotificationName:kMXCallSupportsTransferringStatusDidChange + object:self + userInfo:nil]; } - (void)setState:(MXCallState)state reason:(MXEvent *)event diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 6b82477835..d7b46cc0f7 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -34,6 +34,7 @@ #import "MXCallNegotiateEventContent.h" #import "MXCallReplacesEventContent.h" #import "MXCallRejectReplacementEventContent.h" +#import "MXUserModel.h" #pragma mark - Constants definitions NSString *const kMXCallManagerNewCall = @"kMXCallManagerNewCall"; @@ -635,12 +636,10 @@ - (void)transferCall:(MXCall *)callWithTransferee // find a suitable room (which only consists three users: self, the transferee and the target) - MXWeakify(self); + MXStrongifyAndReturnIfNil(self); [self callTransferRoomWithUsers:@[target.userId, transferee.userId] completion:^(MXRoom * _Nullable transferRoom, BOOL isNewRoom) { - MXStrongifyAndReturnIfNil(self); - if (!transferRoom) { // A room cannot be found/created @@ -747,16 +746,12 @@ - (void)transferCall:(MXCall *)callWithTransferee return; } - MXWeakify(self); - // hold the transferee call before starting the new call [callWithTransferee hold:YES]; // place a new call to the target [self placeCallInRoom:room.roomId withVideo:callWithTransferee.isVideoCall success:^(MXCall * _Nonnull call) { - MXStrongifyAndReturnIfNil(self); - continueBlock(call); } failure:^(NSError * _Nullable error) { NSLog(@"[MXCallManager] transferCall: couldn't call the target: %@", error); From 41d94fa6d0e188a1f8468ec962b8d0f58f7c5954 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 15 Dec 2020 14:53:52 +0300 Subject: [PATCH 59/89] Disable call transfers explicitly for now --- MatrixSDK/VoIP/MXCall.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index fdf3811e3c..c09409d2f3 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -284,6 +284,7 @@ - (void)callWithVideo:(BOOL)video }, @"version": kMXCallVersion, @"lifetime": @(self->callManager.inviteLifetime), + @"capabilities": @{@"m.call.transferee": @(NO)}, // transferring will be disabled until we have a test bridge @"party_id": self.partyId } mutableCopy]; @@ -353,6 +354,7 @@ - (void)answer @"type": kMXCallSessionDescriptionTypeStringAnswer, @"sdp": sdpAnswer }, + @"capabilities": @{@"m.call.transferee": @(NO)}, // transferring will be disabled until we have a test bridge @"version": kMXCallVersion, @"party_id": self.partyId }; From e7c774567004cf448967b31304482e4a7537d112 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 11 Jan 2021 14:05:10 +0300 Subject: [PATCH 60/89] Add supported flag --- MatrixSDK/VoIP/MXCallManager.h | 13 ++++++++++ MatrixSDK/VoIP/MXCallManager.m | 43 ++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/MatrixSDK/VoIP/MXCallManager.h b/MatrixSDK/VoIP/MXCallManager.h index 6ed9bf0ca8..eeeb77edbc 100644 --- a/MatrixSDK/VoIP/MXCallManager.h +++ b/MatrixSDK/VoIP/MXCallManager.h @@ -49,6 +49,12 @@ extern NSString *const kMXCallManagerConferenceStarted; */ extern NSString *const kMXCallManagerConferenceFinished; +/** + Posted when PSTN support has been updated. + The notification object will be the call manager instance. + */ +extern NSString *const kMXCallManagerPSTNSupportUpdated; + /** The `MXCallManager` object manages calls for a given Matrix session. It manages call signaling over Matrix (@see http://matrix.org/docs/spec/#id9) and then opens @@ -216,6 +222,13 @@ extern NSString *const kMXCallManagerConferenceFinished; */ + (BOOL)canPlaceConferenceCallInRoom:(MXRoom *)room roomState:(MXRoomState *)roomState; +#pragma mark - PSTN + +/** + Flag to indicate whether PSTN protocol is supported or not. + */ +@property (nonatomic) BOOL supportsPSTN; + @end NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index d7b46cc0f7..ce70574a30 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -40,6 +40,10 @@ NSString *const kMXCallManagerNewCall = @"kMXCallManagerNewCall"; NSString *const kMXCallManagerConferenceStarted = @"kMXCallManagerConferenceStarted"; NSString *const kMXCallManagerConferenceFinished = @"kMXCallManagerConferenceFinished"; +NSString *const kMXCallManagerPSTNSupportUpdated = @"kMXCallManagerPSTNSupportUpdated"; + +NSString *const kMXProtocolVectorPSTN = @"im.vector.protocol.pstn"; +NSString *const kMXProtocolPSTN = @"m.protocol.pstn"; @interface MXCallManager () @@ -64,6 +68,9 @@ @interface MXCallManager () */ id sessionStateObserver; } + +@property (nonatomic, copy) MXThirdPartyProtocol *pstnProtocol; + @end @@ -114,6 +121,7 @@ - (instancetype)initWithMatrixSession:(MXSession *)mxSession andCallStack:(id Date: Tue, 12 Jan 2021 11:55:11 +0300 Subject: [PATCH 61/89] Add sanity checks --- MatrixSDK/VoIP/MXCallManager.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index ce70574a30..907037b7f5 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -1110,7 +1110,9 @@ - (void)setSupportsPSTN:(BOOL)supportsPSTN - (void)checkPSTNSupport { + MXWeakify(self); [_mxSession.matrixRestClient thirdpartyProtocols:^(MXThirdpartyProtocolsResponse *thirdpartyProtocolsResponse) { + MXStrongifyAndReturnIfNil(self); MXThirdPartyProtocol *protocol = thirdpartyProtocolsResponse.protocols[kMXProtocolVectorPSTN]; From 3133f32077493225b630730fb11ab7ede33d22fa Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 12 Jan 2021 12:02:24 +0300 Subject: [PATCH 62/89] Move models to individual files & implement thirdparty/user/$protocol api --- MatrixSDK.xcodeproj/project.pbxproj | 70 +++++++++++++++++ MatrixSDK/JSONModels/MXJSONModels.h | 78 ------------------- MatrixSDK/JSONModels/MXJSONModels.m | 63 --------------- .../ThirdParty/MXThirdPartyProtocol.h | 50 ++++++++++++ .../ThirdParty/MXThirdPartyProtocol.m | 36 +++++++++ .../ThirdParty/MXThirdPartyProtocolInstance.h | 58 ++++++++++++++ .../ThirdParty/MXThirdPartyProtocolInstance.m | 37 +++++++++ .../ThirdParty/MXThirdPartyUserInstance.h | 40 ++++++++++ .../ThirdParty/MXThirdPartyUserInstance.m | 34 ++++++++ .../ThirdParty/MXThirdPartyUsersResponse.h | 32 ++++++++ .../ThirdParty/MXThirdPartyUsersResponse.m | 33 ++++++++ .../MXThirdpartyProtocolsResponse.h | 36 +++++++++ .../MXThirdpartyProtocolsResponse.m | 39 ++++++++++ MatrixSDK/MXRestClient.h | 18 +++++ MatrixSDK/MXRestClient.m | 35 +++++++++ MatrixSDK/MatrixSDK.h | 4 + MatrixSDK/VoIP/MXCallManager.m | 3 + MatrixSDKTests/MXRestClientTests.m | 2 +- 18 files changed, 526 insertions(+), 142 deletions(-) create mode 100644 MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocol.h create mode 100644 MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocol.m create mode 100644 MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocolInstance.h create mode 100644 MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocolInstance.m create mode 100644 MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUserInstance.h create mode 100644 MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUserInstance.m create mode 100644 MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUsersResponse.h create mode 100644 MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUsersResponse.m create mode 100644 MatrixSDK/JSONModels/ThirdParty/MXThirdpartyProtocolsResponse.h create mode 100644 MatrixSDK/JSONModels/ThirdParty/MXThirdpartyProtocolsResponse.m diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 03e47ee24f..c06bcbb733 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -1177,6 +1177,26 @@ C6FE1EF01E65C4F7008587E4 /* MXAnalyticsDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = C6FE1EEF1E65C4F7008587E4 /* MXAnalyticsDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC619D9224DD834B00663A80 /* MXPushGatewayRestClient.m in Sources */ = {isa = PBXBuildFile; fileRef = EC619D9024DD834B00663A80 /* MXPushGatewayRestClient.m */; }; EC619D9324DD834B00663A80 /* MXPushGatewayRestClient.h in Headers */ = {isa = PBXBuildFile; fileRef = EC619D9124DD834B00663A80 /* MXPushGatewayRestClient.h */; }; + EC87CF5A25AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF5825AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h */; }; + EC87CF5B25AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF5825AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h */; }; + EC87CF5C25AC813500DB0A71 /* MXThirdpartyProtocolsResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF5925AC813500DB0A71 /* MXThirdpartyProtocolsResponse.m */; }; + EC87CF5D25AC813500DB0A71 /* MXThirdpartyProtocolsResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF5925AC813500DB0A71 /* MXThirdpartyProtocolsResponse.m */; }; + EC87CF6825AC816400DB0A71 /* MXThirdPartyProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF6625AC816400DB0A71 /* MXThirdPartyProtocol.h */; }; + EC87CF6925AC816400DB0A71 /* MXThirdPartyProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF6625AC816400DB0A71 /* MXThirdPartyProtocol.h */; }; + EC87CF6A25AC816400DB0A71 /* MXThirdPartyProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF6725AC816400DB0A71 /* MXThirdPartyProtocol.m */; }; + EC87CF6B25AC816400DB0A71 /* MXThirdPartyProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF6725AC816400DB0A71 /* MXThirdPartyProtocol.m */; }; + EC87CF7225AC818700DB0A71 /* MXThirdPartyProtocolInstance.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF7025AC818700DB0A71 /* MXThirdPartyProtocolInstance.h */; }; + EC87CF7325AC818700DB0A71 /* MXThirdPartyProtocolInstance.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF7025AC818700DB0A71 /* MXThirdPartyProtocolInstance.h */; }; + EC87CF7425AC818700DB0A71 /* MXThirdPartyProtocolInstance.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF7125AC818700DB0A71 /* MXThirdPartyProtocolInstance.m */; }; + EC87CF7525AC818700DB0A71 /* MXThirdPartyProtocolInstance.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF7125AC818700DB0A71 /* MXThirdPartyProtocolInstance.m */; }; + EC87CF7C25AC826300DB0A71 /* MXThirdPartyUsersResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF7A25AC826300DB0A71 /* MXThirdPartyUsersResponse.h */; }; + EC87CF7D25AC826300DB0A71 /* MXThirdPartyUsersResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF7A25AC826300DB0A71 /* MXThirdPartyUsersResponse.h */; }; + EC87CF7E25AC826300DB0A71 /* MXThirdPartyUsersResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF7B25AC826300DB0A71 /* MXThirdPartyUsersResponse.m */; }; + EC87CF7F25AC826300DB0A71 /* MXThirdPartyUsersResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF7B25AC826300DB0A71 /* MXThirdPartyUsersResponse.m */; }; + EC87CF8625AC98E200DB0A71 /* MXThirdPartyUserInstance.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF8425AC98E200DB0A71 /* MXThirdPartyUserInstance.h */; }; + EC87CF8725AC98E200DB0A71 /* MXThirdPartyUserInstance.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF8425AC98E200DB0A71 /* MXThirdPartyUserInstance.h */; }; + EC87CF8825AC98E200DB0A71 /* MXThirdPartyUserInstance.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF8525AC98E200DB0A71 /* MXThirdPartyUserInstance.m */; }; + EC87CF8925AC98E200DB0A71 /* MXThirdPartyUserInstance.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF8525AC98E200DB0A71 /* MXThirdPartyUserInstance.m */; }; EC97653B255BFED4000C36EF /* MXCallEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976539255BFED4000C36EF /* MXCallEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC97653C255BFED4000C36EF /* MXCallEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976539255BFED4000C36EF /* MXCallEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC97653D255BFED4000C36EF /* MXCallEventContent.m in Sources */ = {isa = PBXBuildFile; fileRef = EC97653A255BFED4000C36EF /* MXCallEventContent.m */; }; @@ -1906,6 +1926,16 @@ E9B11A2D50665ADBB4B2F5C5 /* Pods-MatrixSDK-MatrixSDK-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixSDK-MatrixSDK-iOS.release.xcconfig"; path = "Target Support Files/Pods-MatrixSDK-MatrixSDK-iOS/Pods-MatrixSDK-MatrixSDK-iOS.release.xcconfig"; sourceTree = ""; }; EC619D9024DD834B00663A80 /* MXPushGatewayRestClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXPushGatewayRestClient.m; sourceTree = ""; }; EC619D9124DD834B00663A80 /* MXPushGatewayRestClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXPushGatewayRestClient.h; sourceTree = ""; }; + EC87CF5825AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXThirdpartyProtocolsResponse.h; sourceTree = ""; }; + EC87CF5925AC813500DB0A71 /* MXThirdpartyProtocolsResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXThirdpartyProtocolsResponse.m; sourceTree = ""; }; + EC87CF6625AC816400DB0A71 /* MXThirdPartyProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXThirdPartyProtocol.h; sourceTree = ""; }; + EC87CF6725AC816400DB0A71 /* MXThirdPartyProtocol.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXThirdPartyProtocol.m; sourceTree = ""; }; + EC87CF7025AC818700DB0A71 /* MXThirdPartyProtocolInstance.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXThirdPartyProtocolInstance.h; sourceTree = ""; }; + EC87CF7125AC818700DB0A71 /* MXThirdPartyProtocolInstance.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXThirdPartyProtocolInstance.m; sourceTree = ""; }; + EC87CF7A25AC826300DB0A71 /* MXThirdPartyUsersResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXThirdPartyUsersResponse.h; sourceTree = ""; }; + EC87CF7B25AC826300DB0A71 /* MXThirdPartyUsersResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXThirdPartyUsersResponse.m; sourceTree = ""; }; + EC87CF8425AC98E200DB0A71 /* MXThirdPartyUserInstance.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXThirdPartyUserInstance.h; sourceTree = ""; }; + EC87CF8525AC98E200DB0A71 /* MXThirdPartyUserInstance.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXThirdPartyUserInstance.m; sourceTree = ""; }; EC976539255BFED4000C36EF /* MXCallEventContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCallEventContent.h; sourceTree = ""; }; EC97653A255BFED4000C36EF /* MXCallEventContent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCallEventContent.m; sourceTree = ""; }; EC976547255BFF35000C36EF /* MXCallInviteEventContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCallInviteEventContent.h; sourceTree = ""; }; @@ -2636,6 +2666,7 @@ 3281E8B219E42DFE00976E1A /* JSONModels */ = { isa = PBXGroup; children = ( + EC87CF5725AC810800DB0A71 /* ThirdParty */, EC976538255BFE46000C36EF /* Call */, 327E9AE422859FE300A98BC1 /* Aggregations */, 323547D12226D3F500F15F94 /* AutoDiscovery */, @@ -3361,6 +3392,23 @@ path = PushGateway; sourceTree = ""; }; + EC87CF5725AC810800DB0A71 /* ThirdParty */ = { + isa = PBXGroup; + children = ( + EC87CF5825AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h */, + EC87CF5925AC813500DB0A71 /* MXThirdpartyProtocolsResponse.m */, + EC87CF6625AC816400DB0A71 /* MXThirdPartyProtocol.h */, + EC87CF6725AC816400DB0A71 /* MXThirdPartyProtocol.m */, + EC87CF7025AC818700DB0A71 /* MXThirdPartyProtocolInstance.h */, + EC87CF7125AC818700DB0A71 /* MXThirdPartyProtocolInstance.m */, + EC87CF7A25AC826300DB0A71 /* MXThirdPartyUsersResponse.h */, + EC87CF7B25AC826300DB0A71 /* MXThirdPartyUsersResponse.m */, + EC87CF8425AC98E200DB0A71 /* MXThirdPartyUserInstance.h */, + EC87CF8525AC98E200DB0A71 /* MXThirdPartyUserInstance.m */, + ); + path = ThirdParty; + sourceTree = ""; + }; EC976538255BFE46000C36EF /* Call */ = { isa = PBXGroup; children = ( @@ -3518,6 +3566,7 @@ 32B0E33F23A378320054FF1A /* MXEventReference.h in Headers */, 32B76EA320FDE2BE00B095F6 /* MXRoomMembersCount.h in Headers */, 32C6F93319DD814400EA4E9C /* MatrixSDK.h in Headers */, + EC87CF8625AC98E200DB0A71 /* MXThirdPartyUserInstance.h in Headers */, 32C474C122AF7A2D00CFBCD2 /* MXReactionOperation.h in Headers */, 324BE46C1E422766008D99D4 /* MXMegolmSessionData.h in Headers */, 324AAC6F239913AD00380A66 /* MXKeyVerificationJSONModel.h in Headers */, @@ -3545,6 +3594,7 @@ 32114A851A262CE000FF2EC4 /* MXStore.h in Headers */, 32BA86AF2152A79E008F277E /* MXRoomNameDefaultStringLocalizations.h in Headers */, 32A151391DAD292400400192 /* MXMegolmEncryption.h in Headers */, + EC87CF6825AC816400DB0A71 /* MXThirdPartyProtocol.h in Headers */, F03EF5041DF01596009DF592 /* MXLRUCache.h in Headers */, B172857C2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.h in Headers */, 329FB1791A0A74B100A5E88E /* MXTools.h in Headers */, @@ -3622,6 +3672,7 @@ 32618E7B20EFA45B00E1D2EA /* MXRoomMembers.h in Headers */, 3240969D1F9F751600DBA607 /* MXPushRuleSenderNotificationPermissionConditionChecker.h in Headers */, 329E808C224E2E1B00A48C3A /* MXOutgoingSASTransaction.h in Headers */, + EC87CF7C25AC826300DB0A71 /* MXThirdPartyUsersResponse.h in Headers */, EC976549255BFF35000C36EF /* MXCallInviteEventContent.h in Headers */, B146D4D521A5A44E00D8C2C6 /* MXScanRealmProvider.h in Headers */, EC97657B255C0101000C36EF /* MXCallCandidate.h in Headers */, @@ -3695,11 +3746,13 @@ B19A309C240424BD00FB6F35 /* MXQRCodeTransaction.h in Headers */, 9274AFE81EE580240009BEB6 /* MXCallKitAdapter.h in Headers */, 321CFDE622525A49004D31DF /* MXSASTransaction.h in Headers */, + EC87CF7225AC818700DB0A71 /* MXThirdPartyProtocolInstance.h in Headers */, B19A30CE24042F0800FB6F35 /* MXSelfVerifyingMasterKeyTrustedQRCodeData.h in Headers */, 324DD2B1246BDC6800377005 /* MXSecretStorage_Private.h in Headers */, 32DC15D01A8CF7AE006F9AD3 /* MXNotificationCenter.h in Headers */, 3275FD9C21A6B60B00B9C13D /* MXLoginPolicy.h in Headers */, 3250E7CA220C913900736CB5 /* MXCryptoTools.h in Headers */, + EC87CF5A25AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h in Headers */, F0173EAC1FCF0E8900B5F6A3 /* MXGroup.h in Headers */, 32D2CC0523422462002BD8CA /* MX3PidAddSession.h in Headers */, 32F00AC02488FB3600131741 /* MXRecoveryService_Private.h in Headers */, @@ -3755,6 +3808,7 @@ B14EF2A72397E90400758AF0 /* MXPushRuleConditionChecker.h in Headers */, 32581DE923C8C0C900832EAA /* MXUserTrustLevel.h in Headers */, B14EF2A82397E90400758AF0 /* MXServiceTermsRestClient.h in Headers */, + EC87CF7325AC818700DB0A71 /* MXThirdPartyProtocolInstance.h in Headers */, B14EF2A92397E90400758AF0 /* MXServerNoticeContent.h in Headers */, B14EF2AA2397E90400758AF0 /* MXOlmDecryption.h in Headers */, B14EF2AB2397E90400758AF0 /* MXUIKitBackgroundTask.h in Headers */, @@ -3787,6 +3841,7 @@ EC97657C255C0101000C36EF /* MXCallCandidate.h in Headers */, B14EF2C02397E90400758AF0 /* MXRoomNameStringsLocalizable.h in Headers */, B14EF2C12397E90400758AF0 /* MXKeyBackup.h in Headers */, + EC87CF7D25AC826300DB0A71 /* MXThirdPartyUsersResponse.h in Headers */, B14EF2C22397E90400758AF0 /* MXCallKitConfiguration.h in Headers */, B19A30BD2404268600FB6F35 /* MXQRCodeDataCoder.h in Headers */, B14EF2C32397E90400758AF0 /* MXFilterJSONModel.h in Headers */, @@ -3810,6 +3865,7 @@ 324AAC832399143400380A66 /* MXKeyVerificationRequestByDMJSONModel.h in Headers */, 324DD2C0246D658500377005 /* MXHkdfSha256.h in Headers */, 8EC5110B256822B400EC4E5B /* MXTaggedEvents.h in Headers */, + EC87CF5B25AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h in Headers */, B14EF2D12397E90400758AF0 /* MXEventsEnumeratorOnArray.h in Headers */, B1DDC9D72418098200D208E3 /* MXIncomingSASTransaction_Private.h in Headers */, B14EF2D22397E90400758AF0 /* MXReplyEventParts.h in Headers */, @@ -3861,6 +3917,7 @@ B14EF2F22397E90400758AF0 /* MXEventScan.h in Headers */, B14EF2F32397E90400758AF0 /* MXRealmMediaScanMapper.h in Headers */, B14EF2F42397E90400758AF0 /* NSData+MatrixSDK.h in Headers */, + EC87CF6925AC816400DB0A71 /* MXThirdPartyProtocol.h in Headers */, B14EF2F52397E90400758AF0 /* MXSDKOptions.h in Headers */, B14EF2F62397E90400758AF0 /* MXJSONModels.h in Headers */, B14EF2F72397E90400758AF0 /* MXRoomEventFilter.h in Headers */, @@ -3889,6 +3946,7 @@ B14EF3092397E90400758AF0 /* MXKey.h in Headers */, B14EF30A2397E90400758AF0 /* MXKeyBackupVersion.h in Headers */, B14EF30B2397E90400758AF0 /* MXWellKnownBaseConfig.h in Headers */, + EC87CF8725AC98E200DB0A71 /* MXThirdPartyUserInstance.h in Headers */, B14EF30C2397E90400758AF0 /* MXDeviceInfo.h in Headers */, 32B0E34023A378320054FF1A /* MXEventReference.h in Headers */, EC97655E255C002A000C36EF /* MXCallAnswerEventContent.h in Headers */, @@ -4363,6 +4421,7 @@ 32C78B6A256CFC4D008130B1 /* MXCryptoMigration.m in Sources */, 32D2CC0623422462002BD8CA /* MX3PidAddManager.m in Sources */, 92634B831EF2E3C400DB9F60 /* MXCallKitConfiguration.m in Sources */, + EC87CF7E25AC826300DB0A71 /* MXThirdPartyUsersResponse.m in Sources */, 3265CB391A14C43E00E24B2F /* MXRoomState.m in Sources */, EC619D9224DD834B00663A80 /* MXPushGatewayRestClient.m in Sources */, 3281E8B819E42DFE00976E1A /* MXJSONModel.m in Sources */, @@ -4459,7 +4518,9 @@ 3294FD9D22F321B0007F1E60 /* MXServiceTermsRestClient.m in Sources */, 3A23A73F256D322C00B9D00F /* MXAes.m in Sources */, 323547DD2226FC5700F15F94 /* MXCredentials.m in Sources */, + EC87CF5C25AC813500DB0A71 /* MXThirdpartyProtocolsResponse.m in Sources */, 32FA10CF1FA1C9F700E54233 /* MXOutgoingRoomKeyRequest.m in Sources */, + EC87CF7425AC818700DB0A71 /* MXThirdPartyProtocolInstance.m in Sources */, 32322A4C1E575F65005DD155 /* MXAllowedCertificates.m in Sources */, F03EF5051DF01596009DF592 /* MXLRUCache.m in Sources */, 32A30B191FB4813400C8309E /* MXIncomingRoomKeyRequestManager.m in Sources */, @@ -4473,6 +4534,7 @@ 327E37B71A974F75007F026F /* MXLogger.m in Sources */, 324BE4691E3FADB1008D99D4 /* MXMegolmExportEncryption.m in Sources */, 32A31BBF20D3F2EC005916C7 /* MXFilterObject.m in Sources */, + EC87CF8825AC98E200DB0A71 /* MXThirdPartyUserInstance.m in Sources */, 320DFDE719DD99B60068622A /* MXHTTPClient.m in Sources */, 32FE41371D0AB7070060835E /* MXEnumConstants.m in Sources */, 320DFDE119DD99B60068622A /* MXSession.m in Sources */, @@ -4534,6 +4596,7 @@ B146D4F721A5BB9F00D8C2C6 /* MXRealmMediaScanStore.m in Sources */, 32999DE422DCD1AD004FF987 /* MXPusherData.m in Sources */, 322A51C81D9BBD3C00C8536D /* MXOlmDevice.m in Sources */, + EC87CF6A25AC816400DB0A71 /* MXThirdPartyProtocol.m in Sources */, 32442FB221EDD21300D2411B /* MXKeyBackupPassword.m in Sources */, 329FB17A1A0A74B100A5E88E /* MXTools.m in Sources */, 329D3E631E251027002E2F1E /* MXRoomSummaryUpdater.m in Sources */, @@ -4660,7 +4723,9 @@ B14EF1E72397E90400758AF0 /* MXRoomThirdPartyInvite.m in Sources */, B14EF1E82397E90400758AF0 /* MXRoomPowerLevels.m in Sources */, B14EF1E92397E90400758AF0 /* MXRealmMediaScanMapper.m in Sources */, + EC87CF7F25AC826300DB0A71 /* MXThirdPartyUsersResponse.m in Sources */, B19A30D724042F2700FB6F35 /* MXSelfVerifyingMasterKeyNotTrustedQRCodeData.m in Sources */, + EC87CF5D25AC813500DB0A71 /* MXThirdpartyProtocolsResponse.m in Sources */, B14EF1EA2397E90400758AF0 /* MXRealmMediaScan.m in Sources */, B14EF1EB2397E90400758AF0 /* MXRoomOperation.m in Sources */, B14EF1EC2397E90400758AF0 /* MXCryptoAlgorithms.m in Sources */, @@ -4753,6 +4818,7 @@ B14EF22F2397E90400758AF0 /* (null) in Sources */, B14EF2302397E90400758AF0 /* MXDeviceListOperation.m in Sources */, 32C78B6B256CFC4D008130B1 /* MXCryptoMigration.m in Sources */, + EC87CF6B25AC816400DB0A71 /* MXThirdPartyProtocol.m in Sources */, B14EF2312397E90400758AF0 /* MX3PidAddSession.m in Sources */, B14EF2322397E90400758AF0 /* MXBugReportRestClient.m in Sources */, B14EF2332397E90400758AF0 /* MXRoomSummary.swift in Sources */, @@ -4786,6 +4852,7 @@ B14EF2482397E90400758AF0 /* MXEncryptedAttachments.m in Sources */, B14EF2492397E90400758AF0 /* MXSendReplyEventDefaultStringLocalizations.m in Sources */, B14EF24A2397E90400758AF0 /* NSArray+MatrixSDK.m in Sources */, + EC87CF7525AC818700DB0A71 /* MXThirdPartyProtocolInstance.m in Sources */, B19A30A92404257700FB6F35 /* MXSASKeyVerificationStart.m in Sources */, 324DD2B9246C21C700377005 /* MXSecretStorageKeyCreationInfo.m in Sources */, B14EF24B2397E90400758AF0 /* MXServiceTermsRestClient.m in Sources */, @@ -4834,6 +4901,7 @@ B14EF2682397E90400758AF0 /* MXFilterJSONModel.m in Sources */, 325AD44223BE3E7500FF5277 /* MXCrossSigningInfo.m in Sources */, B14EF2692397E90400758AF0 /* MXMatrixVersions.m in Sources */, + EC87CF8925AC98E200DB0A71 /* MXThirdPartyUserInstance.m in Sources */, B14EF26A2397E90400758AF0 /* MXReactionCountChangeListener.m in Sources */, B14EF26B2397E90400758AF0 /* MXMegolmBackupCreationInfo.m in Sources */, B14EF26C2397E90400758AF0 /* MXRoom.m in Sources */, @@ -5098,6 +5166,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = FFC02CAD08EAF4683778E451 /* Pods-MatrixSDK-MatrixSDK-iOS.debug.xcconfig */; buildSettings = { + CLANG_ANALYZER_OBJC_UNUSED_IVARS = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; DEFINES_MODULE = YES; @@ -5126,6 +5195,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = E9B11A2D50665ADBB4B2F5C5 /* Pods-MatrixSDK-MatrixSDK-iOS.release.xcconfig */; buildSettings = { + CLANG_ANALYZER_OBJC_UNUSED_IVARS = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; DEFINES_MODULE = YES; diff --git a/MatrixSDK/JSONModels/MXJSONModels.h b/MatrixSDK/JSONModels/MXJSONModels.h index 7897f7b379..3859ecca4c 100644 --- a/MatrixSDK/JSONModels/MXJSONModels.h +++ b/MatrixSDK/JSONModels/MXJSONModels.h @@ -123,84 +123,6 @@ FOUNDATION_EXPORT NSString *const kMX3PIDMediumMSISDN; @end - -/** - This class describes a third party protocol instance. - */ -@interface MXThirdPartyProtocolInstance : MXJSONModel - - /** - The network identifier. - */ - @property (nonatomic) NSString *networkId; - - /** - The fields (domain...). - */ - @property (nonatomic) NSDictionary *fields; - - /** - The instance id. - */ - @property (nonatomic) NSString *instanceId; - - /** - The description. - */ - @property (nonatomic) NSString *desc; - - /** - The dedicated bot. - */ - @property (nonatomic) NSString *botUserId; - - /** - The icon URL. - */ - @property (nonatomic) NSString *icon; - -@end - -/** - This class describes a third party server protocol. - */ -@interface MXThirdPartyProtocol : MXJSONModel - - /** - The user fields (domain, nick, username...). - */ - @property (nonatomic) NSArray *userFields; - - /** - The location fields (domain, channels, room...). - */ - @property (nonatomic) NSArray *locationFields; - - /** - The field types. - */ - @property (nonatomic) NSDictionary* > *fieldTypes; - - /** - The instances. - */ - @property (nonatomic) NSArray *instances; -@end - -/** - `MXThirdpartyProtocolsResponse` represents the response of a thirdpartyProtocols request. - */ -@interface MXThirdpartyProtocolsResponse : MXJSONModel - - /** - Available protocols. - The key is the protocol name; the value, the protocol description. - */ - @property (nonatomic) NSDictionary *protocols; - -@end - - /** Login flow types */ diff --git a/MatrixSDK/JSONModels/MXJSONModels.m b/MatrixSDK/JSONModels/MXJSONModels.m index 4d49632642..549b5ccd42 100644 --- a/MatrixSDK/JSONModels/MXJSONModels.m +++ b/MatrixSDK/JSONModels/MXJSONModels.m @@ -91,69 +91,6 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary } @end - -@implementation MXThirdPartyProtocolInstance - -+ (id)modelFromJSON:(NSDictionary *)JSONDictionary -{ - MXThirdPartyProtocolInstance *thirdpartyProtocolInstance = [[MXThirdPartyProtocolInstance alloc] init]; - if (thirdpartyProtocolInstance) - { - MXJSONModelSetString(thirdpartyProtocolInstance.networkId, JSONDictionary[@"network_id"]); - MXJSONModelSetDictionary(thirdpartyProtocolInstance.fields, JSONDictionary[@"fields"]); - MXJSONModelSetString(thirdpartyProtocolInstance.instanceId, JSONDictionary[@"instance_id"]); - MXJSONModelSetString(thirdpartyProtocolInstance.desc, JSONDictionary[@"desc"]); - MXJSONModelSetString(thirdpartyProtocolInstance.botUserId, JSONDictionary[@"bot_user_id"]); - MXJSONModelSetString(thirdpartyProtocolInstance.icon, JSONDictionary[@"icon"]); - } - - return thirdpartyProtocolInstance; -} - -@end - - -@implementation MXThirdPartyProtocol - -+ (id)modelFromJSON:(NSDictionary *)JSONDictionary -{ - MXThirdPartyProtocol *thirdpartyProtocol = [[MXThirdPartyProtocol alloc] init]; - if (thirdpartyProtocol) - { - MXJSONModelSetArray(thirdpartyProtocol.userFields, JSONDictionary[@"user_fields"]); - MXJSONModelSetArray(thirdpartyProtocol.locationFields, JSONDictionary[@"location_fields"]); - MXJSONModelSetDictionary(thirdpartyProtocol.fieldTypes, JSONDictionary[@"field_types"]); - MXJSONModelSetMXJSONModelArray(thirdpartyProtocol.instances, MXThirdPartyProtocolInstance, JSONDictionary[@"instances"]) - } - - return thirdpartyProtocol; -} - -@end - - -@implementation MXThirdpartyProtocolsResponse - -+ (id)modelFromJSON:(NSDictionary *)JSONDictionary -{ - MXThirdpartyProtocolsResponse *thirdpartyProtocolsResponse = [[MXThirdpartyProtocolsResponse alloc] init]; - if (thirdpartyProtocolsResponse) - { - NSMutableDictionary *protocols = [NSMutableDictionary dictionary]; - for (NSString *protocolName in JSONDictionary) - { - MXJSONModelSetMXJSONModel(protocols[protocolName], MXThirdPartyProtocol, JSONDictionary[protocolName]); - } - - thirdpartyProtocolsResponse.protocols = protocols; - } - - return thirdpartyProtocolsResponse; -} - -@end - - NSString *const kMXLoginFlowTypePassword = @"m.login.password"; NSString *const kMXLoginFlowTypeRecaptcha = @"m.login.recaptcha"; NSString *const kMXLoginFlowTypeOAuth2 = @"m.login.oauth2"; diff --git a/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocol.h b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocol.h new file mode 100644 index 0000000000..8fa6e87e4f --- /dev/null +++ b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocol.h @@ -0,0 +1,50 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@class MXThirdPartyProtocolInstance; + +NS_ASSUME_NONNULL_BEGIN + +/** + This class describes a third party server protocol. + */ +@interface MXThirdPartyProtocol : MXJSONModel + +/** + The user fields (domain, nick, username...). + */ +@property (nonatomic) NSArray *userFields; + +/** + The location fields (domain, channels, room...). + */ +@property (nonatomic) NSArray *locationFields; + +/** + The field types. + */ +@property (nonatomic) NSDictionary* > *fieldTypes; + +/** + The instances. + */ +@property (nonatomic) NSArray *instances; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocol.m b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocol.m new file mode 100644 index 0000000000..82b93de9d3 --- /dev/null +++ b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocol.m @@ -0,0 +1,36 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXThirdPartyProtocol.h" +#import "MXThirdPartyProtocolInstance.h" + +@implementation MXThirdPartyProtocol + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXThirdPartyProtocol *thirdpartyProtocol = [[MXThirdPartyProtocol alloc] init]; + if (thirdpartyProtocol) + { + MXJSONModelSetArray(thirdpartyProtocol.userFields, JSONDictionary[@"user_fields"]); + MXJSONModelSetArray(thirdpartyProtocol.locationFields, JSONDictionary[@"location_fields"]); + MXJSONModelSetDictionary(thirdpartyProtocol.fieldTypes, JSONDictionary[@"field_types"]); + MXJSONModelSetMXJSONModelArray(thirdpartyProtocol.instances, MXThirdPartyProtocolInstance, JSONDictionary[@"instances"]); + } + + return thirdpartyProtocol; +} + +@end diff --git a/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocolInstance.h b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocolInstance.h new file mode 100644 index 0000000000..91f620e747 --- /dev/null +++ b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocolInstance.h @@ -0,0 +1,58 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + This class describes a third party protocol instance. + */ +@interface MXThirdPartyProtocolInstance : MXJSONModel + +/** + The network identifier. + */ +@property (nonatomic) NSString *networkId; + +/** + The fields (domain...). + */ +@property (nonatomic) NSDictionary *fields; + +/** + The instance id. + */ +@property (nonatomic) NSString *instanceId; + +/** + The description. + */ +@property (nonatomic) NSString *desc; + +/** + The dedicated bot. + */ +@property (nonatomic) NSString *botUserId; + +/** + The icon URL. + */ +@property (nonatomic) NSString *icon; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocolInstance.m b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocolInstance.m new file mode 100644 index 0000000000..467df9cdca --- /dev/null +++ b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyProtocolInstance.m @@ -0,0 +1,37 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXThirdPartyProtocolInstance.h" + +@implementation MXThirdPartyProtocolInstance + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXThirdPartyProtocolInstance *thirdpartyProtocolInstance = [[MXThirdPartyProtocolInstance alloc] init]; + if (thirdpartyProtocolInstance) + { + MXJSONModelSetString(thirdpartyProtocolInstance.networkId, JSONDictionary[@"network_id"]); + MXJSONModelSetDictionary(thirdpartyProtocolInstance.fields, JSONDictionary[@"fields"]); + MXJSONModelSetString(thirdpartyProtocolInstance.instanceId, JSONDictionary[@"instance_id"]); + MXJSONModelSetString(thirdpartyProtocolInstance.desc, JSONDictionary[@"desc"]); + MXJSONModelSetString(thirdpartyProtocolInstance.botUserId, JSONDictionary[@"bot_user_id"]); + MXJSONModelSetString(thirdpartyProtocolInstance.icon, JSONDictionary[@"icon"]); + } + + return thirdpartyProtocolInstance; +} + +@end diff --git a/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUserInstance.h b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUserInstance.h new file mode 100644 index 0000000000..7e66542655 --- /dev/null +++ b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUserInstance.h @@ -0,0 +1,40 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MXThirdPartyUserInstance : MXJSONModel + +/** + A Matrix User ID represting a third party user. + */ +@property (nonatomic) NSString *userId; + +/** + The protocol ID that the third party user is a part of. + */ +@property (nonatomic) NSString *protocol; + +/** + Information used to identify this third party user. + */ +@property (nonatomic) NSDictionary *fields; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUserInstance.m b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUserInstance.m new file mode 100644 index 0000000000..393a43c402 --- /dev/null +++ b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUserInstance.m @@ -0,0 +1,34 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXThirdPartyUserInstance.h" + +@implementation MXThirdPartyUserInstance + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXThirdPartyUserInstance *response = [[MXThirdPartyUserInstance alloc] init]; + if (response) + { + MXJSONModelSetString(response.userId, JSONDictionary[@"userid"]); + MXJSONModelSetString(response.protocol, JSONDictionary[@"protocol"]); + MXJSONModelSetDictionary(response.fields, JSONDictionary[@"fields"]); + } + + return response; +} + +@end diff --git a/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUsersResponse.h b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUsersResponse.h new file mode 100644 index 0000000000..29fd195134 --- /dev/null +++ b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUsersResponse.h @@ -0,0 +1,32 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@class MXThirdPartyUserInstance; + +NS_ASSUME_NONNULL_BEGIN + +@interface MXThirdPartyUsersResponse : MXJSONModel + +/** + Third party users. + */ +@property (nonatomic) NSArray *users; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUsersResponse.m b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUsersResponse.m new file mode 100644 index 0000000000..8807e79432 --- /dev/null +++ b/MatrixSDK/JSONModels/ThirdParty/MXThirdPartyUsersResponse.m @@ -0,0 +1,33 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXThirdPartyUsersResponse.h" +#import "MXThirdPartyUserInstance.h" + +@implementation MXThirdPartyUsersResponse + ++ (instancetype)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXThirdPartyUsersResponse *response = [[MXThirdPartyUsersResponse alloc] init]; + if (response) + { + MXJSONModelSetMXJSONModelArray(response.users, MXThirdPartyUserInstance, (NSArray *)JSONDictionary); + } + + return response; +} + +@end diff --git a/MatrixSDK/JSONModels/ThirdParty/MXThirdpartyProtocolsResponse.h b/MatrixSDK/JSONModels/ThirdParty/MXThirdpartyProtocolsResponse.h new file mode 100644 index 0000000000..4ac5a3ab63 --- /dev/null +++ b/MatrixSDK/JSONModels/ThirdParty/MXThirdpartyProtocolsResponse.h @@ -0,0 +1,36 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@class MXThirdPartyProtocol; + +NS_ASSUME_NONNULL_BEGIN + +/** + `MXThirdpartyProtocolsResponse` represents the response of a thirdpartyProtocols request. + */ +@interface MXThirdpartyProtocolsResponse : MXJSONModel + +/** + Available protocols. + The key is the protocol name; the value, the protocol description. + */ +@property (nonatomic) NSDictionary *protocols; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/JSONModels/ThirdParty/MXThirdpartyProtocolsResponse.m b/MatrixSDK/JSONModels/ThirdParty/MXThirdpartyProtocolsResponse.m new file mode 100644 index 0000000000..0e467503b5 --- /dev/null +++ b/MatrixSDK/JSONModels/ThirdParty/MXThirdpartyProtocolsResponse.m @@ -0,0 +1,39 @@ +// +// Copyright 2020 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MXThirdpartyProtocolsResponse.h" +#import "MXThirdPartyProtocol.h" + +@implementation MXThirdpartyProtocolsResponse + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXThirdpartyProtocolsResponse *thirdpartyProtocolsResponse = [[MXThirdpartyProtocolsResponse alloc] init]; + if (thirdpartyProtocolsResponse) + { + NSMutableDictionary *protocols = [NSMutableDictionary dictionary]; + for (NSString *protocolName in JSONDictionary) + { + MXJSONModelSetMXJSONModel(protocols[protocolName], MXThirdPartyProtocol, JSONDictionary[protocolName]); + } + + thirdpartyProtocolsResponse.protocols = protocols; + } + + return thirdpartyProtocolsResponse; +} + +@end diff --git a/MatrixSDK/MXRestClient.h b/MatrixSDK/MXRestClient.h index e48a6ea20d..9e97d22498 100644 --- a/MatrixSDK/MXRestClient.h +++ b/MatrixSDK/MXRestClient.h @@ -42,6 +42,9 @@ #import "MXRoomCreationParameters.h" #import "MXTurnServerResponse.h" +@class MXThirdpartyProtocolsResponse; +@class MXThirdPartyUsersResponse; + #pragma mark - Constants definitions /** A constant representing the URI path for release 0 of the Client-Server HTTP API. @@ -1879,6 +1882,21 @@ typedef MXHTTPOperation* (^MXRestClientIdentityServerAccessTokenHandler)(void (^ - (MXHTTPOperation*)thirdpartyProtocols:(void (^)(MXThirdpartyProtocolsResponse *thirdpartyProtocolsResponse))success failure:(void (^)(NSError *error))failure; +/** + Retrieve a Matrix User ID linked to a user on the third party service, given a set of user parameters. + + @param protocol Required. The name of the protocol. + @param fields One or more custom fields that are passed to the AS to help identify the user. + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return an MXHTTPOperation instance. + */ +- (MXHTTPOperation*)thirdpartyUsers:(NSString *)protocol + fields:(NSDictionary *)fields + success:(void (^)(MXThirdPartyUsersResponse *thirdpartyUsersResponse))success + failure:(void (^)(NSError *error))failure; + #pragma mark - Media Repository API /** diff --git a/MatrixSDK/MXRestClient.m b/MatrixSDK/MXRestClient.m index 2c5caf3c15..124a7dbd4b 100644 --- a/MatrixSDK/MXRestClient.m +++ b/MatrixSDK/MXRestClient.m @@ -26,6 +26,9 @@ #import "MXAllowedCertificates.h" +#import "MXThirdpartyProtocolsResponse.h" +#import "MXThirdPartyUsersResponse.h" + #pragma mark - Constants definitions /** Prefix used in path of home server API requests. @@ -3597,6 +3600,38 @@ - (MXHTTPOperation*)thirdpartyProtocols:(void (^)(MXThirdpartyProtocolsResponse }]; } +- (MXHTTPOperation *)thirdpartyUsers:(NSString *)protocol fields:(NSDictionary *)fields success:(void (^)(MXThirdPartyUsersResponse *))success failure:(void (^)(NSError *))failure +{ + NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:1]; + + if (fields) + { + params[@"fields"] = fields; + } + + MXWeakify(self); + return [httpClient requestWithMethod:@"GET" + path:[NSString stringWithFormat:@"%@/thirdparty/user/%@", kMXAPIPrefixPathUnstable, protocol] + parameters:params + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + + if (success) + { + __block MXThirdPartyUsersResponse *thirdpartyUsersResponse; + [self dispatchProcessing:^{ + thirdpartyUsersResponse = [MXThirdPartyUsersResponse modelFromJSON:JSONResponse]; + } andCompletion:^{ + success(thirdpartyUsersResponse); + }]; + } + } + failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + #pragma mark - Media Repository API - (MXHTTPOperation*) uploadContent:(NSData *)data diff --git a/MatrixSDK/MatrixSDK.h b/MatrixSDK/MatrixSDK.h index d9b71426a1..b7f4a98607 100644 --- a/MatrixSDK/MatrixSDK.h +++ b/MatrixSDK/MatrixSDK.h @@ -113,3 +113,7 @@ FOUNDATION_EXPORT NSString *MatrixSDKVersion; #import "MXCallReplacesEventContent.h" #import "MXUserModel.h" #import "MXCallCapabilitiesModel.h" + +#import "MXThirdPartyProtocolInstance.h" +#import "MXThirdPartyProtocol.h" +#import "MXThirdpartyProtocolsResponse.h" diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 907037b7f5..c00a0a558b 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -36,6 +36,9 @@ #import "MXCallRejectReplacementEventContent.h" #import "MXUserModel.h" +#import "MXThirdPartyProtocol.h" +#import "MXThirdpartyProtocolsResponse.h" + #pragma mark - Constants definitions NSString *const kMXCallManagerNewCall = @"kMXCallManagerNewCall"; NSString *const kMXCallManagerConferenceStarted = @"kMXCallManagerConferenceStarted"; diff --git a/MatrixSDKTests/MXRestClientTests.m b/MatrixSDKTests/MXRestClientTests.m index a01113fff9..b8e5cd6e4b 100644 --- a/MatrixSDKTests/MXRestClientTests.m +++ b/MatrixSDKTests/MXRestClientTests.m @@ -22,7 +22,7 @@ #import "MatrixSDKTestsData.h" #import "MXRoomMember.h" #import "MXKey.h" - +#import "MXThirdpartyProtocolsResponse.h" // Do not bother with retain cycles warnings in tests #pragma clang diagnostic push From fb54ff451156ec214eb451e3b2162bf7c4949ed9 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 12 Jan 2021 14:13:38 +0300 Subject: [PATCH 63/89] Fix mac build --- MatrixSDK.xcodeproj/project.pbxproj | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index c06bcbb733..8caef9ebcb 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -1177,24 +1177,24 @@ C6FE1EF01E65C4F7008587E4 /* MXAnalyticsDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = C6FE1EEF1E65C4F7008587E4 /* MXAnalyticsDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC619D9224DD834B00663A80 /* MXPushGatewayRestClient.m in Sources */ = {isa = PBXBuildFile; fileRef = EC619D9024DD834B00663A80 /* MXPushGatewayRestClient.m */; }; EC619D9324DD834B00663A80 /* MXPushGatewayRestClient.h in Headers */ = {isa = PBXBuildFile; fileRef = EC619D9124DD834B00663A80 /* MXPushGatewayRestClient.h */; }; - EC87CF5A25AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF5825AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h */; }; - EC87CF5B25AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF5825AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h */; }; + EC87CF5A25AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF5825AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC87CF5B25AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF5825AC813500DB0A71 /* MXThirdpartyProtocolsResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC87CF5C25AC813500DB0A71 /* MXThirdpartyProtocolsResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF5925AC813500DB0A71 /* MXThirdpartyProtocolsResponse.m */; }; EC87CF5D25AC813500DB0A71 /* MXThirdpartyProtocolsResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF5925AC813500DB0A71 /* MXThirdpartyProtocolsResponse.m */; }; - EC87CF6825AC816400DB0A71 /* MXThirdPartyProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF6625AC816400DB0A71 /* MXThirdPartyProtocol.h */; }; - EC87CF6925AC816400DB0A71 /* MXThirdPartyProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF6625AC816400DB0A71 /* MXThirdPartyProtocol.h */; }; + EC87CF6825AC816400DB0A71 /* MXThirdPartyProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF6625AC816400DB0A71 /* MXThirdPartyProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC87CF6925AC816400DB0A71 /* MXThirdPartyProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF6625AC816400DB0A71 /* MXThirdPartyProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC87CF6A25AC816400DB0A71 /* MXThirdPartyProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF6725AC816400DB0A71 /* MXThirdPartyProtocol.m */; }; EC87CF6B25AC816400DB0A71 /* MXThirdPartyProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF6725AC816400DB0A71 /* MXThirdPartyProtocol.m */; }; - EC87CF7225AC818700DB0A71 /* MXThirdPartyProtocolInstance.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF7025AC818700DB0A71 /* MXThirdPartyProtocolInstance.h */; }; - EC87CF7325AC818700DB0A71 /* MXThirdPartyProtocolInstance.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF7025AC818700DB0A71 /* MXThirdPartyProtocolInstance.h */; }; + EC87CF7225AC818700DB0A71 /* MXThirdPartyProtocolInstance.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF7025AC818700DB0A71 /* MXThirdPartyProtocolInstance.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC87CF7325AC818700DB0A71 /* MXThirdPartyProtocolInstance.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF7025AC818700DB0A71 /* MXThirdPartyProtocolInstance.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC87CF7425AC818700DB0A71 /* MXThirdPartyProtocolInstance.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF7125AC818700DB0A71 /* MXThirdPartyProtocolInstance.m */; }; EC87CF7525AC818700DB0A71 /* MXThirdPartyProtocolInstance.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF7125AC818700DB0A71 /* MXThirdPartyProtocolInstance.m */; }; - EC87CF7C25AC826300DB0A71 /* MXThirdPartyUsersResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF7A25AC826300DB0A71 /* MXThirdPartyUsersResponse.h */; }; - EC87CF7D25AC826300DB0A71 /* MXThirdPartyUsersResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF7A25AC826300DB0A71 /* MXThirdPartyUsersResponse.h */; }; + EC87CF7C25AC826300DB0A71 /* MXThirdPartyUsersResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF7A25AC826300DB0A71 /* MXThirdPartyUsersResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC87CF7D25AC826300DB0A71 /* MXThirdPartyUsersResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF7A25AC826300DB0A71 /* MXThirdPartyUsersResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC87CF7E25AC826300DB0A71 /* MXThirdPartyUsersResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF7B25AC826300DB0A71 /* MXThirdPartyUsersResponse.m */; }; EC87CF7F25AC826300DB0A71 /* MXThirdPartyUsersResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF7B25AC826300DB0A71 /* MXThirdPartyUsersResponse.m */; }; - EC87CF8625AC98E200DB0A71 /* MXThirdPartyUserInstance.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF8425AC98E200DB0A71 /* MXThirdPartyUserInstance.h */; }; - EC87CF8725AC98E200DB0A71 /* MXThirdPartyUserInstance.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF8425AC98E200DB0A71 /* MXThirdPartyUserInstance.h */; }; + EC87CF8625AC98E200DB0A71 /* MXThirdPartyUserInstance.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF8425AC98E200DB0A71 /* MXThirdPartyUserInstance.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC87CF8725AC98E200DB0A71 /* MXThirdPartyUserInstance.h in Headers */ = {isa = PBXBuildFile; fileRef = EC87CF8425AC98E200DB0A71 /* MXThirdPartyUserInstance.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC87CF8825AC98E200DB0A71 /* MXThirdPartyUserInstance.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF8525AC98E200DB0A71 /* MXThirdPartyUserInstance.m */; }; EC87CF8925AC98E200DB0A71 /* MXThirdPartyUserInstance.m in Sources */ = {isa = PBXBuildFile; fileRef = EC87CF8525AC98E200DB0A71 /* MXThirdPartyUserInstance.m */; }; EC97653B255BFED4000C36EF /* MXCallEventContent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC976539255BFED4000C36EF /* MXCallEventContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; From e06580f62642186c3facdb35407334ba65457941 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 12 Jan 2021 14:24:55 +0300 Subject: [PATCH 64/89] Introduce a method to call a phone number --- MatrixSDK/VoIP/MXCallManager.h | 13 ++++++++++ MatrixSDK/VoIP/MXCallManager.m | 47 ++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/MatrixSDK/VoIP/MXCallManager.h b/MatrixSDK/VoIP/MXCallManager.h index eeeb77edbc..8b1fbc50fb 100644 --- a/MatrixSDK/VoIP/MXCallManager.h +++ b/MatrixSDK/VoIP/MXCallManager.h @@ -229,6 +229,19 @@ extern NSString *const kMXCallManagerPSTNSupportUpdated; */ @property (nonatomic) BOOL supportsPSTN; +/** + Place a voice or a video call into a room. + + @param phoneNumber the phone number against to place the call. + @param video YES to make a video call. + @param success A block object called when the operation succeeds. It provides the created MXCall instance. + @param failure A block object called when the operation fails. + */ +- (void)placeCallAgainst:(NSString *)phoneNumber + withVideo:(BOOL)video + success:(void (^)(MXCall *call))success + failure:(void (^)(NSError * _Nullable error))failure; + @end NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index c00a0a558b..acda2dc677 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -38,6 +38,8 @@ #import "MXThirdPartyProtocol.h" #import "MXThirdpartyProtocolsResponse.h" +#import "MXThirdPartyUsersResponse.h" +#import "MXThirdPartyUserInstance.h" #pragma mark - Constants definitions NSString *const kMXCallManagerNewCall = @"kMXCallManagerNewCall"; @@ -45,6 +47,7 @@ NSString *const kMXCallManagerConferenceFinished = @"kMXCallManagerConferenceFinished"; NSString *const kMXCallManagerPSTNSupportUpdated = @"kMXCallManagerPSTNSupportUpdated"; +// TODO: Replace usages of this with `kMXProtocolPSTN` when MSC completed NSString *const kMXProtocolVectorPSTN = @"im.vector.protocol.pstn"; NSString *const kMXProtocolPSTN = @"m.protocol.pstn"; @@ -1132,4 +1135,48 @@ - (void)checkPSTNSupport }]; } +- (void)placeCallAgainst:(NSString *)phoneNumber + withVideo:(BOOL)video + success:(void (^)(MXCall * _Nonnull))success + failure:(void (^)(NSError * _Nullable))failure +{ + [_mxSession.matrixRestClient thirdpartyUsers:kMXProtocolVectorPSTN + fields:@{ + kMXLoginIdentifierTypePhone: phoneNumber + } + success:^(MXThirdPartyUsersResponse *thirdpartyUsersResponse) { + + MXThirdPartyUserInstance * user = [thirdpartyUsersResponse.users firstObject]; + + NSLog(@"Succeeded to look up the phone number: %@", user.userId); + + // try to find a direct room with this user + [self directCallableRoomWithUser:user.userId completion:^(MXRoom * _Nullable room, NSError * _Nullable error) { + if (room) + { + // room found, place the call in this room + [self placeCallInRoom:room.roomId + withVideo:video + success:success + failure:failure]; + } + else + { + // no room found + NSLog(@"Failed to find a room for call with error: %@", error); + if (failure) + { + failure(error); + } + } + }]; + } failure:^(NSError *error) { + NSLog(@"Failed to look up the phone number with error: %@", error); + if (failure) + { + failure(error); + } + }]; +} + @end From 1e1e9b8793d1325b9d3a5453509438911e7fe1d7 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 14 Jan 2021 16:21:39 +0300 Subject: [PATCH 65/89] Handle no user case --- MatrixSDK/VoIP/MXCallManager.m | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index acda2dc677..4101795380 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -1150,6 +1150,15 @@ - (void)placeCallAgainst:(NSString *)phoneNumber NSLog(@"Succeeded to look up the phone number: %@", user.userId); + if (user == nil) + { + if (failure) + { + failure(nil); + } + return; + } + // try to find a direct room with this user [self directCallableRoomWithUser:user.userId completion:^(MXRoom * _Nullable room, NSError * _Nullable error) { if (room) From a0d3a35afc7c5beb8920655ecf121090d6a60128 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 14 Jan 2021 18:01:52 +0300 Subject: [PATCH 66/89] Add DTMF support --- MatrixSDK/VoIP/CallStack/MXCallStackCall.h | 34 +++++++++++ MatrixSDK/VoIP/MXCall.h | 35 ++++++++++++ MatrixSDK/VoIP/MXCall.m | 14 +++++ .../VoIP/Jingle/MXJingleCallStackCall.m | 56 +++++++++++++++++++ 4 files changed, 139 insertions(+) diff --git a/MatrixSDK/VoIP/CallStack/MXCallStackCall.h b/MatrixSDK/VoIP/CallStack/MXCallStackCall.h index 383dd5cd17..5972161bf6 100644 --- a/MatrixSDK/VoIP/CallStack/MXCallStackCall.h +++ b/MatrixSDK/VoIP/CallStack/MXCallStackCall.h @@ -140,6 +140,40 @@ NS_ASSUME_NONNULL_BEGIN success:(void (^)(void))success failure:(void (^)(NSError *error))failure; +#pragma mark - DTMF + +/** + Indicates whether this call can send DTMF tones. + This property will be false if the call is not connected yet. + */ +@property (nonatomic, readonly) BOOL canSendDTMF; + +/** + * Queues a task that sends the DTMF tones. The tones parameter is treated + * as a series of characters. The characters 0 through 9, A through D, #, and * + * generate the associated DTMF tones. The characters a to d are equivalent + * to A to D. The character ',' indicates a delay of 2 seconds before + * processing the next character in the tones parameter. + * + * Unrecognized characters are ignored. + * + * @param duration The parameter indicates the duration to use for each + * character passed in the tones parameter (in milliseconds). The duration cannot be more + * than 6000 or less than 70 ms. If given value is outside of these limits, it'll be limited to these values. + * Pass 0 to use default value or last used value. + * + * @param interToneGap The parameter indicates the gap between tones (in milliseconds). + * This parameter must be at least 50 ms but should be as short as + * possible. If given value is lower than 50 ms, it'll be ignored. + * Pass 0 to use default value or last used value. + * + * If InsertDtmf is called on the same object while an existing task for this + * object to generate DTMF is still running, the previous task is canceled. + * Returns true on success and false on failure. + */ +- (BOOL)sendDTMF:(NSString * _Nonnull)tones + duration:(NSTimeInterval)duration + interToneGap:(NSTimeInterval)interToneGap; #pragma mark - Properties /** diff --git a/MatrixSDK/VoIP/MXCall.h b/MatrixSDK/VoIP/MXCall.h index 418e9e6a24..4875fcb3d1 100644 --- a/MatrixSDK/VoIP/MXCall.h +++ b/MatrixSDK/VoIP/MXCall.h @@ -184,6 +184,41 @@ extern NSString *const kMXCallSupportsTransferringStatusDidChange; success:(void (^)(NSString * _Nonnull eventId))success failure:(void (^)(NSError * _Nullable error))failure; +#pragma mark - DTMF + +/** + Indicates whether this call can send DTMF tones. + This property will be false if the call is not connected yet. + */ +@property (nonatomic, readonly) BOOL supportsDTMF; + +/** + * Queues a task that sends the DTMF tones. The tones parameter is treated + * as a series of characters. The characters 0 through 9, A through D, #, and * + * generate the associated DTMF tones. The characters a to d are equivalent + * to A to D. The character ',' indicates a delay of 2 seconds before + * processing the next character in the tones parameter. + * + * Unrecognized characters are ignored. + * + * @param duration The parameter indicates the duration to use for each + * character passed in the tones parameter (in milliseconds). The duration cannot be more + * than 6000 or less than 70 ms. If given value is outside of these limits, it'll be limited to these values. + * Pass 0 to use default value or last used value. + * + * @param interToneGap The parameter indicates the gap between tones (in milliseconds). + * This parameter must be at least 50 ms but should be as short as + * possible. If given value is lower than 50 ms, it'll be ignored. + * Pass 0 to use default value or last used value. + * + * If InsertDtmf is called on the same object while an existing task for this + * object to generate DTMF is still running, the previous task is canceled. + * Returns true on success and false on failure. + */ +- (BOOL)sendDTMF:(NSString * _Nonnull)tones + duration:(NSTimeInterval)duration + interToneGap:(NSTimeInterval)interToneGap; + #pragma mark - Properties /** The room where the call is placed. diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index c09409d2f3..623b3f3a8f 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -608,6 +608,20 @@ - (void)transferToRoom:(NSString * _Nullable)targetRoomId }]; } +#pragma mark - DTMF + +- (BOOL)supportsDTMF +{ + return [callStackCall canSendDTMF]; +} + +- (BOOL)sendDTMF:(NSString * _Nonnull)tones + duration:(NSTimeInterval)duration + interToneGap:(NSTimeInterval)interToneGap +{ + return [callStackCall sendDTMF:tones duration:duration interToneGap:interToneGap]; +} + #pragma mark - Properties - (void)setSelectedAnswer:(MXEvent *)selectedAnswer diff --git a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m index 99db18ba75..5781a1fe3b 100644 --- a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m +++ b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m @@ -393,6 +393,62 @@ - (void)handleAnswer:(NSString *)sdp success:(void (^)(void))success failure:(vo }]; } +#pragma mark - DTMF + +- (BOOL)canSendDTMF +{ + id dtmfSender = peerConnection.senders.firstObject.dtmfSender; + + if (dtmfSender == nil) + { + return NO; + } + + return dtmfSender.canInsertDtmf; +} + +- (BOOL)sendDTMF:(NSString *)tones + duration:(NSTimeInterval)duration + interToneGap:(NSTimeInterval)interToneGap; +{ + if (!self.canSendDTMF) + { + // cannot send DTMF + return NO; + } + + id dtmfSender = peerConnection.senders.firstObject.dtmfSender; + + if (duration == 0) + { + // use default or last used value + duration = dtmfSender.duration; + } + else if (duration < 70) + { + // limit lower bound + duration = 70; + } + else if (duration > 6000) + { + // limit upper bound + duration = 6000; + } + + if (interToneGap == 0) + { + // use default or last used value + interToneGap = dtmfSender.interToneGap; + } + else if (interToneGap < 50) + { + // limit lower bound + interToneGap = 50; + } + + return [dtmfSender insertDtmf:tones duration:duration interToneGap:interToneGap]; +} + #pragma mark - RTCPeerConnectionDelegate // Triggered when the SignalingState changed. From 5db2c23b94dd0fbccbc58c0ffaf64d91990d1257 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 14 Jan 2021 18:03:00 +0300 Subject: [PATCH 67/89] Update CHANGES.rst --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 40cbc9cebf..536b0c5918 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,7 @@ Changes to be released in next version * Add hold support for CallKit calls (vector-im/element-ios/issues/3834). * Fix video call with web (vector-im/element-ios/issues/3862). * VoIP: Call transfers initiation (vector-im/element-ios/issues/3872). + * VoIP: DTMF support in calls (vector-im/element-ios/issues/3929). 🐛 Bugfix * From c08f04d2adcbf7f0a4b90a130bbaff6b76bb4ea9 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 14 Jan 2021 21:49:36 +0300 Subject: [PATCH 68/89] Rewrite DTMF method comments --- MatrixSDK/VoIP/CallStack/MXCallStackCall.h | 34 +++++++--------------- MatrixSDK/VoIP/MXCall.h | 34 +++++++--------------- 2 files changed, 22 insertions(+), 46 deletions(-) diff --git a/MatrixSDK/VoIP/CallStack/MXCallStackCall.h b/MatrixSDK/VoIP/CallStack/MXCallStackCall.h index 5972161bf6..26fe7e9098 100644 --- a/MatrixSDK/VoIP/CallStack/MXCallStackCall.h +++ b/MatrixSDK/VoIP/CallStack/MXCallStackCall.h @@ -148,29 +148,17 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, readonly) BOOL canSendDTMF; -/** - * Queues a task that sends the DTMF tones. The tones parameter is treated - * as a series of characters. The characters 0 through 9, A through D, #, and * - * generate the associated DTMF tones. The characters a to d are equivalent - * to A to D. The character ',' indicates a delay of 2 seconds before - * processing the next character in the tones parameter. - * - * Unrecognized characters are ignored. - * - * @param duration The parameter indicates the duration to use for each - * character passed in the tones parameter (in milliseconds). The duration cannot be more - * than 6000 or less than 70 ms. If given value is outside of these limits, it'll be limited to these values. - * Pass 0 to use default value or last used value. - * - * @param interToneGap The parameter indicates the gap between tones (in milliseconds). - * This parameter must be at least 50 ms but should be as short as - * possible. If given value is lower than 50 ms, it'll be ignored. - * Pass 0 to use default value or last used value. - * - * If InsertDtmf is called on the same object while an existing task for this - * object to generate DTMF is still running, the previous task is canceled. - * Returns true on success and false on failure. - */ +/// Creates a task to send given DTMF tones in the call. If there is a task already running, it'll be canceled. +/// @param tones DTMF tones to be sent. Allowed characters: [0-9], [A-D], '#', `*`. Case insensitive. Comma (',') will cause a 2 seconds delay before sending next character. +/// @param duration Duration for each character of tones (in milliseconds). +/// Allowed interval is from 70 ms to 6000 ms inclusively. +/// If given value is outside of these limits, it'll be limited to them. +/// Pass 0 to use default value or last used value. +/// @param interToneGap Duration for gap between each character of tones (in milliseconds). +/// Must be at least 50 ms. +/// If given value is lower than 50 ms, it'll be limited to that value. +/// Pass 0 to use default value or last used value. +/// @returns Whether the operation succeeded or not. - (BOOL)sendDTMF:(NSString * _Nonnull)tones duration:(NSTimeInterval)duration interToneGap:(NSTimeInterval)interToneGap; diff --git a/MatrixSDK/VoIP/MXCall.h b/MatrixSDK/VoIP/MXCall.h index 4875fcb3d1..a7c55b5781 100644 --- a/MatrixSDK/VoIP/MXCall.h +++ b/MatrixSDK/VoIP/MXCall.h @@ -192,29 +192,17 @@ extern NSString *const kMXCallSupportsTransferringStatusDidChange; */ @property (nonatomic, readonly) BOOL supportsDTMF; -/** - * Queues a task that sends the DTMF tones. The tones parameter is treated - * as a series of characters. The characters 0 through 9, A through D, #, and * - * generate the associated DTMF tones. The characters a to d are equivalent - * to A to D. The character ',' indicates a delay of 2 seconds before - * processing the next character in the tones parameter. - * - * Unrecognized characters are ignored. - * - * @param duration The parameter indicates the duration to use for each - * character passed in the tones parameter (in milliseconds). The duration cannot be more - * than 6000 or less than 70 ms. If given value is outside of these limits, it'll be limited to these values. - * Pass 0 to use default value or last used value. - * - * @param interToneGap The parameter indicates the gap between tones (in milliseconds). - * This parameter must be at least 50 ms but should be as short as - * possible. If given value is lower than 50 ms, it'll be ignored. - * Pass 0 to use default value or last used value. - * - * If InsertDtmf is called on the same object while an existing task for this - * object to generate DTMF is still running, the previous task is canceled. - * Returns true on success and false on failure. - */ +/// Creates a task to send given DTMF tones in the call. If there is a task already running, it'll be canceled. +/// @param tones DTMF tones to be sent. Allowed characters: [0-9], [A-D], '#', `*`. Case insensitive. Comma (',') will cause a 2 seconds delay before sending next character. +/// @param duration Duration for each character of tones (in milliseconds). +/// Allowed interval is from 70 ms to 6000 ms inclusively. +/// If given value is outside of these limits, it'll be limited to them. +/// Pass 0 to use default value or last used value. +/// @param interToneGap Duration for gap between each character of tones (in milliseconds). +/// Must be at least 50 ms. +/// If given value is lower than 50 ms, it'll be limited to that value. +/// Pass 0 to use default value or last used value. +/// @returns Whether the operation succeeded or not. - (BOOL)sendDTMF:(NSString * _Nonnull)tones duration:(NSTimeInterval)duration interToneGap:(NSTimeInterval)interToneGap; From d530f276e98a66348e89e30fc923031618f7fe38 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 14 Jan 2021 21:59:11 +0300 Subject: [PATCH 69/89] Improve bounds check for dtmf method --- .../VoIP/Jingle/MXJingleCallStackCall.m | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m index 5781a1fe3b..b3e2efcde4 100644 --- a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m +++ b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m @@ -424,15 +424,13 @@ - (BOOL)sendDTMF:(NSString *)tones // use default or last used value duration = dtmfSender.duration; } - else if (duration < 70) + else { // limit lower bound - duration = 70; - } - else if (duration > 6000) - { + duration = MAX(duration, 70); + // limit upper bound - duration = 6000; + duration = MIN(duration, 6000); } if (interToneGap == 0) @@ -440,10 +438,10 @@ - (BOOL)sendDTMF:(NSString *)tones // use default or last used value interToneGap = dtmfSender.interToneGap; } - else if (interToneGap < 50) + else { // limit lower bound - interToneGap = 50; + interToneGap = MAX(interToneGap, 50); } return [dtmfSender insertDtmf:tones duration:duration interToneGap:interToneGap]; From d397f285ee02ba4cbb7a5dba97f50ab048762e81 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 15 Jan 2021 11:33:01 +0300 Subject: [PATCH 70/89] Fix Manu's comments --- MatrixSDK/VoIP/CallStack/MXCallStackCall.h | 28 ++++++++++--------- MatrixSDK/VoIP/MXCall.h | 28 ++++++++++--------- MatrixSDK/VoIP/MXCall.m | 4 +-- .../VoIP/Jingle/MXJingleCallStackCall.m | 4 +-- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/MatrixSDK/VoIP/CallStack/MXCallStackCall.h b/MatrixSDK/VoIP/CallStack/MXCallStackCall.h index 26fe7e9098..35cf014eb3 100644 --- a/MatrixSDK/VoIP/CallStack/MXCallStackCall.h +++ b/MatrixSDK/VoIP/CallStack/MXCallStackCall.h @@ -148,20 +148,22 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, readonly) BOOL canSendDTMF; -/// Creates a task to send given DTMF tones in the call. If there is a task already running, it'll be canceled. -/// @param tones DTMF tones to be sent. Allowed characters: [0-9], [A-D], '#', `*`. Case insensitive. Comma (',') will cause a 2 seconds delay before sending next character. -/// @param duration Duration for each character of tones (in milliseconds). -/// Allowed interval is from 70 ms to 6000 ms inclusively. -/// If given value is outside of these limits, it'll be limited to them. -/// Pass 0 to use default value or last used value. -/// @param interToneGap Duration for gap between each character of tones (in milliseconds). -/// Must be at least 50 ms. -/// If given value is lower than 50 ms, it'll be limited to that value. -/// Pass 0 to use default value or last used value. -/// @returns Whether the operation succeeded or not. +/** + Creates a task to send given DTMF tones in the call. If there is a task already running, it'll be canceled. + @param tones DTMF tones to be sent. Allowed characters: [0-9], [A-D], '#', `*`. Case insensitive. Comma (',') will cause a 2 seconds delay before sending next character. + @param duration Duration for each character of tones (in milliseconds). + Allowed interval is from 70 ms to 6000 ms inclusively. + If given value is outside of these limits, it'll be limited to them. + Pass 0 to use default value or last used value. + @param interToneGap Duration for gap between each character of tones (in milliseconds). + Must be at least 50 ms. + If given value is lower than 50 ms, it'll be limited to that value. + Pass 0 to use default value or last used value. + @returns Whether the operation succeeded or not. + */ - (BOOL)sendDTMF:(NSString * _Nonnull)tones - duration:(NSTimeInterval)duration - interToneGap:(NSTimeInterval)interToneGap; + duration:(NSUInteger)duration + interToneGap:(NSUInteger)interToneGap; #pragma mark - Properties /** diff --git a/MatrixSDK/VoIP/MXCall.h b/MatrixSDK/VoIP/MXCall.h index a7c55b5781..003e460e1a 100644 --- a/MatrixSDK/VoIP/MXCall.h +++ b/MatrixSDK/VoIP/MXCall.h @@ -192,20 +192,22 @@ extern NSString *const kMXCallSupportsTransferringStatusDidChange; */ @property (nonatomic, readonly) BOOL supportsDTMF; -/// Creates a task to send given DTMF tones in the call. If there is a task already running, it'll be canceled. -/// @param tones DTMF tones to be sent. Allowed characters: [0-9], [A-D], '#', `*`. Case insensitive. Comma (',') will cause a 2 seconds delay before sending next character. -/// @param duration Duration for each character of tones (in milliseconds). -/// Allowed interval is from 70 ms to 6000 ms inclusively. -/// If given value is outside of these limits, it'll be limited to them. -/// Pass 0 to use default value or last used value. -/// @param interToneGap Duration for gap between each character of tones (in milliseconds). -/// Must be at least 50 ms. -/// If given value is lower than 50 ms, it'll be limited to that value. -/// Pass 0 to use default value or last used value. -/// @returns Whether the operation succeeded or not. +/** + Creates a task to send given DTMF tones in the call. If there is a task already running, it'll be canceled. + @param tones DTMF tones to be sent. Allowed characters: [0-9], [A-D], '#', `*`. Case insensitive. Comma (',') will cause a 2 seconds delay before sending next character. + @param duration Duration for each character of tones (in milliseconds). + Allowed interval is from 70 ms to 6000 ms inclusively. + If given value is outside of these limits, it'll be limited to them. + Pass 0 to use default value or last used value. + @param interToneGap Duration for gap between each character of tones (in milliseconds). + Must be at least 50 ms. + If given value is lower than 50 ms, it'll be limited to that value. + Pass 0 to use default value or last used value. + @returns Whether the operation succeeded or not. + */ - (BOOL)sendDTMF:(NSString * _Nonnull)tones - duration:(NSTimeInterval)duration - interToneGap:(NSTimeInterval)interToneGap; + duration:(NSUInteger)duration + interToneGap:(NSUInteger)interToneGap; #pragma mark - Properties /** diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 623b3f3a8f..19150e0c48 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -616,8 +616,8 @@ - (BOOL)supportsDTMF } - (BOOL)sendDTMF:(NSString * _Nonnull)tones - duration:(NSTimeInterval)duration - interToneGap:(NSTimeInterval)interToneGap + duration:(NSUInteger)duration + interToneGap:(NSUInteger)interToneGap { return [callStackCall sendDTMF:tones duration:duration interToneGap:interToneGap]; } diff --git a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m index b3e2efcde4..bb9f551129 100644 --- a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m +++ b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m @@ -408,8 +408,8 @@ - (BOOL)canSendDTMF } - (BOOL)sendDTMF:(NSString *)tones - duration:(NSTimeInterval)duration - interToneGap:(NSTimeInterval)interToneGap; + duration:(NSUInteger)duration + interToneGap:(NSUInteger)interToneGap; { if (!self.canSendDTMF) { From a7222b33e18d1f09ccb2c9cff9231ee33c88dbe7 Mon Sep 17 00:00:00 2001 From: Giom Foret Date: Sat, 16 Jan 2021 22:06:27 +0100 Subject: [PATCH 71/89] BugFix - MXRoomSummary: directUserId may be missing (null) for a direct chat if it was joined on another device. I observed a direct chat for which the field directUserId was null in its room summary. NOK This field of the room summaries is updated currently in 3 cases (at the sdk level during the sync): - initial sync - new invites in the server sync response - an update of the direct chats (in the account data) I remembered join this direct chat on a web client, I realized the room summary was not correctly updated on iOS because the room was not present in the rooms list when we processed the new direct chat list. Indeed the account data (in which are the directs) are handled before the joined room list... In conclusion: directUserId is missing (null) for a direct chat after the sync if it was joined on another device whereas the ios session was offline. --- CHANGES.rst | 2 +- MatrixSDK/MXSession.m | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index fd9d53408b..edaec4cbb6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,7 +8,7 @@ Changes to be released in next version * 🐛 Bugfix - * + * MXRoomSummary: directUserId may be missing (null) for a direct chat if it was joined on another device. ⚠️ API Changes * diff --git a/MatrixSDK/MXSession.m b/MatrixSDK/MXSession.m index 6c80d359cb..cba73fac62 100644 --- a/MatrixSDK/MXSession.m +++ b/MatrixSDK/MXSession.m @@ -2420,6 +2420,14 @@ - (void)addRoom:(MXRoom*)room notify:(BOOL)notify { summary = [[MXRoomSummary alloc] initWithRoomId:room.roomId andMatrixSession:self]; roomsSummaries[room.roomId] = summary; + + // Update the summary if necessary + NSString *directUserId = [self directUserIdInRoom:room.roomId]; + if (directUserId) + { + summary.directUserId = directUserId; + [summary save:YES]; + } } if (notify) From 54bda9bb2e2bec9b4eff14cce624269f44334a29 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 21 Jan 2021 01:29:35 +0300 Subject: [PATCH 72/89] Fix thirdparty/user/$protocol api --- MatrixSDK/MXRestClient.h | 2 +- MatrixSDK/MXRestClient.m | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/MatrixSDK/MXRestClient.h b/MatrixSDK/MXRestClient.h index 392142bb1c..5ae0648897 100644 --- a/MatrixSDK/MXRestClient.h +++ b/MatrixSDK/MXRestClient.h @@ -1886,7 +1886,7 @@ typedef MXHTTPOperation* (^MXRestClientIdentityServerAccessTokenHandler)(void (^ Retrieve a Matrix User ID linked to a user on the third party service, given a set of user parameters. @param protocol Required. The name of the protocol. - @param fields One or more custom fields that are passed to the AS to help identify the user. + @param fields One or more custom fields that are passed to the AS to help identify the user. Not optional. @param success A block object called when the operation succeeds. @param failure A block object called when the operation fails. diff --git a/MatrixSDK/MXRestClient.m b/MatrixSDK/MXRestClient.m index 69711b9343..d5cf52abf3 100644 --- a/MatrixSDK/MXRestClient.m +++ b/MatrixSDK/MXRestClient.m @@ -3602,17 +3602,10 @@ - (MXHTTPOperation*)thirdpartyProtocols:(void (^)(MXThirdpartyProtocolsResponse - (MXHTTPOperation *)thirdpartyUsers:(NSString *)protocol fields:(NSDictionary *)fields success:(void (^)(MXThirdPartyUsersResponse *))success failure:(void (^)(NSError *))failure { - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:1]; - - if (fields) - { - params[@"fields"] = fields; - } - MXWeakify(self); return [httpClient requestWithMethod:@"GET" path:[NSString stringWithFormat:@"%@/thirdparty/user/%@", kMXAPIPrefixPathUnstable, protocol] - parameters:params + parameters:fields success:^(NSDictionary *JSONResponse) { MXStrongifyAndReturnIfNil(self); From cf9d12df0367349041126a7bd68e8c416fc7ede2 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 21 Jan 2021 01:30:02 +0300 Subject: [PATCH 73/89] Check call event version type to suppress false warnings --- MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m b/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m index 26a4810afd..4bc099bb5e 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m +++ b/MatrixSDK/JSONModels/Call/Events/MXCallEventContent.m @@ -36,8 +36,14 @@ + (void)initialize - (void)parseJSON:(NSDictionary *)JSONDictionary { MXJSONModelSetString(self.callId, JSONDictionary[@"call_id"]); - MXJSONModelSetNumber(self.versionNumber, JSONDictionary[@"version"]); - MXJSONModelSetString(self.versionString, JSONDictionary[@"version"]); + if ([JSONDictionary[@"version"] isKindOfClass:NSNumber.class]) + { + MXJSONModelSetNumber(self.versionNumber, JSONDictionary[@"version"]); + } + if ([JSONDictionary[@"version"] isKindOfClass:NSString.class]) + { + MXJSONModelSetString(self.versionString, JSONDictionary[@"version"]); + } MXJSONModelSetString(self.partyId, JSONDictionary[@"party_id"]); } From 20c762621bf41dc428810e6319146e2f566edca7 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 22 Jan 2021 14:18:26 +0300 Subject: [PATCH 74/89] Introduce get thirdparty user from phone number method on call manager --- MatrixSDK/VoIP/MXCallManager.h | 12 ++++++++++ MatrixSDK/VoIP/MXCallManager.m | 43 ++++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/MatrixSDK/VoIP/MXCallManager.h b/MatrixSDK/VoIP/MXCallManager.h index 8b1fbc50fb..d02de7b788 100644 --- a/MatrixSDK/VoIP/MXCallManager.h +++ b/MatrixSDK/VoIP/MXCallManager.h @@ -27,6 +27,7 @@ NS_ASSUME_NONNULL_BEGIN @class MXTurnServerResponse; @class MXEvent; @class MXUserModel; +@class MXThirdPartyUserInstance; @protocol MXCallStack; @@ -229,6 +230,17 @@ extern NSString *const kMXCallManagerPSTNSupportUpdated; */ @property (nonatomic) BOOL supportsPSTN; +/** + Get thirdparty user from a phone number. + + @param phoneNumber the phone number against to place the call. + @param success A block object called when the operation succeeds. It provides the user. + @param failure A block object called when the operation fails. + */ +- (void)getThirdPartyUserFrom:(NSString *)phoneNumber + success:(void (^)(MXThirdPartyUserInstance * _Nonnull))success + failure:(void (^)(NSError * _Nullable))failure; + /** Place a voice or a video call into a room. diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 4101795380..e26c8f2ee3 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -1135,10 +1135,9 @@ - (void)checkPSTNSupport }]; } -- (void)placeCallAgainst:(NSString *)phoneNumber - withVideo:(BOOL)video - success:(void (^)(MXCall * _Nonnull))success - failure:(void (^)(NSError * _Nullable))failure +- (void)getThirdPartyUserFrom:(NSString *)phoneNumber + success:(void (^)(MXThirdPartyUserInstance * _Nonnull))success + failure:(void (^)(NSError * _Nullable))failure { [_mxSession.matrixRestClient thirdpartyUsers:kMXProtocolVectorPSTN fields:@{ @@ -1150,14 +1149,37 @@ - (void)placeCallAgainst:(NSString *)phoneNumber NSLog(@"Succeeded to look up the phone number: %@", user.userId); - if (user == nil) + if (user) + { + if (success) + { + success(user); + } + } + else { if (failure) { failure(nil); } - return; } + } failure:^(NSError *error) { + NSLog(@"Failed to look up the phone number with error: %@", error); + if (failure) + { + failure(error); + } + }]; +} + +- (void)placeCallAgainst:(NSString *)phoneNumber + withVideo:(BOOL)video + success:(void (^)(MXCall * _Nonnull))success + failure:(void (^)(NSError * _Nullable))failure +{ + MXWeakify(self); + [self getThirdPartyUserFrom:phoneNumber success:^(MXThirdPartyUserInstance *_Nonnull user) { + MXStrongifyAndReturnIfNil(self); // try to find a direct room with this user [self directCallableRoomWithUser:user.userId completion:^(MXRoom * _Nullable room, NSError * _Nullable error) { @@ -1179,13 +1201,8 @@ - (void)placeCallAgainst:(NSString *)phoneNumber } } }]; - } failure:^(NSError *error) { - NSLog(@"Failed to look up the phone number with error: %@", error); - if (failure) - { - failure(error); - } - }]; + + } failure:failure]; } @end From 9d3153ecc5da85921b31fc847876b76ef6f966b9 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 22 Jan 2021 19:22:12 +0300 Subject: [PATCH 75/89] Introduce --- MatrixSDK/VoIP/MXCallManager.h | 14 ++++++++ MatrixSDK/VoIP/MXCallManager.m | 63 ++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/MatrixSDK/VoIP/MXCallManager.h b/MatrixSDK/VoIP/MXCallManager.h index d02de7b788..bf83f897bd 100644 --- a/MatrixSDK/VoIP/MXCallManager.h +++ b/MatrixSDK/VoIP/MXCallManager.h @@ -28,6 +28,7 @@ NS_ASSUME_NONNULL_BEGIN @class MXEvent; @class MXUserModel; @class MXThirdPartyUserInstance; +@class MXUser; @protocol MXCallStack; @@ -254,6 +255,19 @@ extern NSString *const kMXCallManagerPSTNSupportUpdated; success:(void (^)(MXCall *call))success failure:(void (^)(NSError * _Nullable error))failure; +#pragma mark - Recent + +/** + Get recent contacts with whom a call was present, either incoming or outgoing. + Result will be descending order according to the call time. + So most recent call's contact will be at the beginning of the result. + + @param maxNumberOfUsers Maximum number of desired users. Please note that return value could be less than this. + @param ignoredUserIds Ignored user ids for desired users. + */ +- (NSArray * _Nonnull)getRecentCalledUsers:(NSUInteger)maxNumberOfUsers + ignoredUserIds:(NSArray * _Nullable)ignoredUserIds; + @end NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index e26c8f2ee3..dbb46dc40e 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -1205,4 +1205,67 @@ - (void)placeCallAgainst:(NSString *)phoneNumber } failure:failure]; } +#pragma mark - Recent + +- (NSArray * _Nonnull)getRecentCalledUsers:(NSUInteger)maxNumberOfUsers + ignoredUserIds:(NSArray * _Nullable)ignoredUserIds +{ + if (maxNumberOfUsers == 0) + { + return NSArray.array; + } + + NSArray *rooms = _mxSession.rooms; + + if (rooms.count == 0) + { + return NSArray.array; + } + + NSMutableArray *callEvents = [NSMutableArray arrayWithCapacity:rooms.count]; + + for (MXRoom *room in rooms) { + id enumerator = [room enumeratorForStoredMessagesWithTypeIn:@[kMXEventTypeStringCallInvite]]; + MXEvent *callEvent = enumerator.nextEvent; + if (callEvent) + { + [callEvents addObject:callEvent]; + } + } + + [callEvents sortUsingComparator:^NSComparisonResult(MXEvent * _Nonnull event1, MXEvent * _Nonnull event2) { + return [@(event1.age) compare:@(event2.age)]; + }]; + + NSMutableArray *users = [NSMutableArray arrayWithCapacity:callEvents.count]; + + for (MXEvent *event in callEvents) { + NSString *userId = nil; + if ([event.sender isEqualToString:_mxSession.myUserId]) + { + userId = [_mxSession directUserIdInRoom:event.roomId]; + } + else + { + userId = event.sender; + } + + if (userId && ![ignoredUserIds containsObject:userId]) + { + MXUser *user = [_mxSession userWithUserId:userId]; + if (user) + { + [users addObject:user]; + } + } + } + + if (users.count > maxNumberOfUsers) + { + users = [[users subarrayWithRange:NSMakeRange(0, maxNumberOfUsers)] mutableCopy]; + } + + return users; +} + @end From 25d22acaf63952d22d463ee696a47d4b9c18d50b Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Fri, 22 Jan 2021 19:38:00 +0300 Subject: [PATCH 76/89] Improve number of users check --- MatrixSDK/VoIP/MXCallManager.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index dbb46dc40e..6474a83182 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -1256,15 +1256,15 @@ - (void)placeCallAgainst:(NSString *)phoneNumber if (user) { [users addObject:user]; + if (users.count == maxNumberOfUsers) + { + // no need to go further + break; + } } } } - if (users.count > maxNumberOfUsers) - { - users = [[users subarrayWithRange:NSMakeRange(0, maxNumberOfUsers)] mutableCopy]; - } - return users; } From 864698dc68e333a10057f0b23969dd0b35322abd Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 1 Feb 2021 15:46:38 +0300 Subject: [PATCH 77/89] End call at stack level when call is ended --- MatrixSDK/VoIP/MXCall.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 19150e0c48..390b7d34b8 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -682,6 +682,9 @@ - (void)setState:(MXCallState)state reason:(MXEvent *)event [[MXSDKOptions sharedInstance].analyticsDelegate trackValue:@(_endReason) category:kMXAnalyticsVoipCategory name:kMXAnalyticsVoipNameCallEnded]; + + // Terminate the call at the stack level + [callStackCall end]; } else if (MXCallStateInviteSent == state) { From 3e65d2df80506200bd2236d7d43454cd18798c04 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Mon, 1 Feb 2021 15:48:03 +0300 Subject: [PATCH 78/89] Update CHANGES.rst --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 1b6b81c25a..35f5a60b81 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -12,7 +12,7 @@ Changes to be released in next version * VoIP: DTMF support in calls (vector-im/element-ios/issues/3929). 🐛 Bugfix - * + * VoIP: Fix camera indicator when video call answered elsewhere (vector-im/element-ios/issues/3971). ⚠️ API Changes * From 55eb3efbcfa29702ddd6d6ee5d8a5d5837c3dede Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 3 Feb 2021 14:24:35 +0300 Subject: [PATCH 79/89] Add new event types to map --- MatrixSDK/Utils/MXTools.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MatrixSDK/Utils/MXTools.m b/MatrixSDK/Utils/MXTools.m index cb414a70c5..bbfa9e30e8 100644 --- a/MatrixSDK/Utils/MXTools.m +++ b/MatrixSDK/Utils/MXTools.m @@ -110,6 +110,8 @@ + (void)initialize kMXEventTypeStringCallHangup, kMXEventTypeStringCallReject, kMXEventTypeStringCallNegotiate, + kMXEventTypeStringCallReplaces, + kMXEventTypeStringCallRejectReplacement, kMXEventTypeStringSticker, kMXEventTypeStringRoomTombStone, kMXEventTypeStringKeyVerificationRequest, From cc44319fa4f6780cef4256ca60caec91b86bc044 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 3 Feb 2021 18:25:56 +0100 Subject: [PATCH 80/89] Prepare for new sprint --- CHANGES.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 0db25cc384..986c84bc89 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,27 @@ +Changes to be released in next version +================================================= + +✨ Features + * + +🙌 Improvements + * + +🐛 Bugfix + * + +⚠️ API Changes + * + +🗣 Translations + * + +🧱 Build + * + +Others + * + Changes in 0.17.11 (2021-02-03) ================================================= From ee7eb396e8017957c583e65284cb3620491f356d Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 4 Feb 2021 17:23:20 +0300 Subject: [PATCH 81/89] Implement waiting for other party's join when new room created on call transfer --- MatrixSDK/VoIP/MXCallManager.m | 57 +++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 6474a83182..5b46089cfe 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -51,6 +51,8 @@ NSString *const kMXProtocolVectorPSTN = @"im.vector.protocol.pstn"; NSString *const kMXProtocolPSTN = @"m.protocol.pstn"; +NSTimeInterval const kMXCallDirectRoomJoinTimeout = 30; + @interface MXCallManager () { @@ -746,7 +748,7 @@ - (void)transferCall:(MXCall *)callWithTransferee { MXWeakify(self); - [self directCallableRoomWithUser:target.userId completion:^(MXRoom * _Nullable room, NSError * _Nullable error) { + [self directCallableRoomWithUser:target.userId timeout:kMXCallDirectRoomJoinTimeout completion:^(MXRoom * _Nullable room, NSError * _Nullable error) { MXStrongifyAndReturnIfNil(self); @@ -851,15 +853,57 @@ - (void)callTransferRoomWithUsers:(NSArray *)userIds /// Tries to find a direct & callable room with the given user. If not such a room found, tries to create it and then waits for the other party to join. /// @param userId The user id to check. +/// @param timeout The timeout for the invited user to join the room, in case of the room is newly created (in seconds). /// @param completion Completion block. - (void)directCallableRoomWithUser:(NSString * _Nonnull)userId + timeout:(NSTimeInterval)timeout completion:(void (^_Nonnull)(MXRoom* _Nullable room, NSError * _Nullable error))completion { MXRoom *room = [self.mxSession directJoinedRoomWithUserId:userId]; if (room) { - // TODO: Having a room probably does not mean it's callable (both party joined). Fix this. - completion(room, nil); + [room state:^(MXRoomState *roomState) { + MXMembership membership = [roomState.members memberWithUserId:userId].membership; + + if (membership == MXMembershipJoin) + { + // other party already joined, return the room + completion(room, nil); + } + else if (membership == MXMembershipInvite) + { + // Wait for other party to join before returning the room + __block BOOL joined = NO; + + __block id listener = [room listenToEventsOfTypes:@[kMXEventTypeStringRoomMember] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { + if ([event.sender isEqualToString:userId]) + { + MXRoomMemberEventContent *content = [MXRoomMemberEventContent modelFromJSON:event.content]; + if (content.membership == kMXMembershipStringJoin) + { + joined = YES; + [room removeListener:listener]; + dispatch_async(dispatch_get_main_queue(), ^{ + completion(room, nil); + }); + } + } + }]; + + // implement the timeout + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeout * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + if (!joined) + { + // user failed to join within the given time + completion(nil, nil); + } + }); + } + else + { + completion(nil, nil); + } + }]; } else { @@ -877,9 +921,8 @@ - (void)directCallableRoomWithUser:(NSString * _Nonnull)userId } [self.mxSession createRoomWithParameters:roomCreationParameters success:^(MXRoom *room) { - // TODO: Wait for other party to join before returning the room - - completion(room, nil); + // wait for other party to join + return [self directCallableRoomWithUser:userId timeout:timeout completion:completion]; } failure:^(NSError *error) { completion(nil, error); }]; @@ -1182,7 +1225,7 @@ - (void)placeCallAgainst:(NSString *)phoneNumber MXStrongifyAndReturnIfNil(self); // try to find a direct room with this user - [self directCallableRoomWithUser:user.userId completion:^(MXRoom * _Nullable room, NSError * _Nullable error) { + [self directCallableRoomWithUser:user.userId timeout:kMXCallDirectRoomJoinTimeout completion:^(MXRoom * _Nullable room, NSError * _Nullable error) { if (room) { // room found, place the call in this room From 58601022cb326edd92bd81492dbc2f7215e3a409 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 9 Feb 2021 12:31:59 +0300 Subject: [PATCH 82/89] Update Jitsi to 3.1.0 --- MatrixSDK.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK.podspec b/MatrixSDK.podspec index 56ecaf581b..d9a6545b95 100644 --- a/MatrixSDK.podspec +++ b/MatrixSDK.podspec @@ -55,7 +55,7 @@ Pod::Spec.new do |s| #ss.ios.dependency 'GoogleWebRTC', '~>1.1.21820' # Use WebRTC framework included in Jitsi Meet SDK - ss.ios.dependency 'JitsiMeetSDK', ' 2.11.0' + ss.ios.dependency 'JitsiMeetSDK', ' 3.1.0' # JitsiMeetSDK has not yet binaries for arm64 simulator ss.pod_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' } From 9673e08eb8dcbe633da765458515bf2b3a23419f Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 9 Feb 2021 12:32:57 +0300 Subject: [PATCH 83/89] Update CHANGES.rst --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 986c84bc89..0b353d48e8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,7 @@ Changes to be released in next version * 🙌 Improvements - * + * Pods: Update JitsiMeetSDK to 3.1.0. 🐛 Bugfix * From b153afaadd14099a3a65adc29fc08041bad7b068 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 9 Feb 2021 15:56:11 +0300 Subject: [PATCH 84/89] Update to new Jitsi SDK --- MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m index bb9f551129..28d0f88d76 100644 --- a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m +++ b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m @@ -116,7 +116,7 @@ - (void)hold:(BOOL)hold [peerConnection.transceivers enumerateObjectsUsingBlock:^(RTCRtpTransceiver * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { obj.sender.track.isEnabled = !hold; obj.receiver.track.isEnabled = !hold; - obj.direction = hold ? RTCRtpTransceiverDirectionInactive : RTCRtpTransceiverDirectionSendRecv; + [obj setDirection:hold ? RTCRtpTransceiverDirectionInactive : RTCRtpTransceiverDirectionSendRecv error:NULL]; }]; RTCMediaConstraints *mediaConstraints; From cc352d6b968ca9b969678462211cc28252fb3b45 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 9 Feb 2021 16:02:13 +0300 Subject: [PATCH 85/89] Fix lifetime field on negotiation answers --- .../JSONModels/Call/Events/MXCallNegotiateEventContent.h | 1 + MatrixSDK/VoIP/MXCall.m | 3 +-- MatrixSDK/VoIP/MXCallManager.m | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h index a93d6ffc38..f21d4f9232 100644 --- a/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h +++ b/MatrixSDK/JSONModels/Call/Events/MXCallNegotiateEventContent.h @@ -35,6 +35,7 @@ NS_ASSUME_NONNULL_BEGIN The time in milliseconds that the invite is valid for. Once the invite age exceeds this value, clients should discard it. They should also no longer show the call as awaiting an answer in the UI. + 0 means no lifetime provided. For instance, it's an answer, no need for a lifetime. */ @property (nonatomic) NSUInteger lifetime; diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 390b7d34b8..a9cd5d6bb9 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -1167,8 +1167,7 @@ - (void)handleCallNegotiate:(MXEvent *)event @"sdp": sdpAnswer }, @"version": kMXCallVersion, - @"party_id": self.partyId, - @"lifetime": @(self->callManager.negotiateLifetime) + @"party_id": self.partyId }; [self.callSignalingRoom sendEventOfType:kMXEventTypeStringCallNegotiate content:content localEcho:nil success:nil failure:^(NSError *error) { NSLog(@"[MXCall] handleCallNegotiate: negotiate answer: ERROR: Cannot send m.call.negotiate event."); diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 5b46089cfe..6fc7743308 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -511,8 +511,8 @@ - (void)handleCallNegotiate:(MXEvent *)event { MXCallNegotiateEventContent *content = [MXCallNegotiateEventContent modelFromJSON:event.content]; - // Check expiration (useful filter when receiving load of events when resuming the event stream) - if (event.age < content.lifetime) + // Check expiration if provided (useful filter when receiving load of events when resuming the event stream) + if (content.lifetime == 0 || event.age < content.lifetime) { if ([event.sender isEqualToString:_mxSession.myUserId] && [content.partyId isEqualToString:_mxSession.myDeviceId]) From 2b7ae307b7f7af64c1e527543e2ab84f7cd1f4aa Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Tue, 9 Feb 2021 16:02:45 +0300 Subject: [PATCH 86/89] Check the call is remotely held after handling remote's answer --- MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m index 28d0f88d76..7692285324 100644 --- a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m +++ b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m @@ -373,7 +373,11 @@ - (void)createOffer:(void (^)(NSString *sdp))success failure:(void (^)(NSError * - (void)handleAnswer:(NSString *)sdp success:(void (^)(void))success failure:(void (^)(NSError *))failure { RTCSessionDescription *sessionDescription = [[RTCSessionDescription alloc] initWithType:RTCSdpTypeAnswer sdp:sdp]; + + MXWeakify(self); [peerConnection setRemoteDescription:sessionDescription completionHandler:^(NSError * _Nullable error) { + MXStrongifyAndReturnIfNil(self); + NSLog(@"[MXJingleCallStackCall] handleAnswer: setRemoteDescription: error: %@", error); // Return on main thread @@ -390,6 +394,9 @@ - (void)handleAnswer:(NSString *)sdp success:(void (^)(void))success failure:(vo }); + // check we can consider this call as held, after handling the remote's answer + [self checkTheCallIsRemotelyOnHold]; + }]; } From 212db56f84a7d4dcda2ab088b04a28f08c8be9b7 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 10 Feb 2021 12:22:50 +0300 Subject: [PATCH 87/89] Fix build --- MatrixSDK.xcodeproj/project.pbxproj | 2 ++ MatrixSDK/MatrixSDK.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index c6589d8323..751664dccd 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -1314,6 +1314,7 @@ EC8A53E925B1BCC6004E0802 /* MXThirdpartyProtocolsResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EC8A53D525B1BCC6004E0802 /* MXThirdpartyProtocolsResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; ECAE7AEC24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = ECAE7AEB24ED75F1002FA813 /* HTTPAdditionalHeadersTests.m */; }; ECB5D98C2552C9B4000AD89C /* MXStopwatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECB5D98B2552C9B4000AD89C /* MXStopwatch.swift */; }; + ECD623FF25D3DCC900DC0A0B /* MXOlmDecryption.h in Headers */ = {isa = PBXBuildFile; fileRef = 327187831DA7D0220071C818 /* MXOlmDecryption.h */; settings = {ATTRIBUTES = (Public, ); }; }; F0173EAC1FCF0E8900B5F6A3 /* MXGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = F0173EAA1FCF0E8800B5F6A3 /* MXGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; F0173EAD1FCF0E8900B5F6A3 /* MXGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = F0173EAB1FCF0E8900B5F6A3 /* MXGroup.m */; }; F03EF4FE1DF014D9009DF592 /* MXMediaLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = F03EF4FA1DF014D9009DF592 /* MXMediaLoader.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -4094,6 +4095,7 @@ B14EF2E82397E90400758AF0 /* MXSendReplyEventDefaultStringLocalizations.h in Headers */, B14EF2E92397E90400758AF0 /* MXTools.h in Headers */, B14EF2EA2397E90400758AF0 /* MXDeviceListOperation.h in Headers */, + ECD623FF25D3DCC900DC0A0B /* MXOlmDecryption.h in Headers */, B14EF2EB2397E90400758AF0 /* MXRoomAccountData.h in Headers */, B14EF2EC2397E90400758AF0 /* MXEventContentRelatesTo.h in Headers */, B19A30BF2404268600FB6F35 /* MXQRCodeData.h in Headers */, diff --git a/MatrixSDK/MatrixSDK.h b/MatrixSDK/MatrixSDK.h index 68cadcee3c..bf4a37c40b 100644 --- a/MatrixSDK/MatrixSDK.h +++ b/MatrixSDK/MatrixSDK.h @@ -117,6 +117,8 @@ FOUNDATION_EXPORT NSString *MatrixSDKVersion; #import "MXThirdPartyProtocolInstance.h" #import "MXThirdPartyProtocol.h" #import "MXThirdpartyProtocolsResponse.h" +#import "MXThirdPartyUserInstance.h" +#import "MXThirdPartyUsersResponse.h" #import "MXLoginSSOFlow.h" From 883b2398f3fa599324a41eda2ed9a2c384ae147b Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 11 Feb 2021 13:22:56 +0300 Subject: [PATCH 88/89] version++ --- CHANGES.rst | 2 +- MatrixSDK.podspec | 2 +- MatrixSDK/MatrixSDKVersion.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index f1b7ada7d0..a7227e46c9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,4 +1,4 @@ -Changes to be released in next version +Changes in 0.18.0 (2021-02-11) ================================================= ✨ Features diff --git a/MatrixSDK.podspec b/MatrixSDK.podspec index d9a6545b95..4c46e2c522 100644 --- a/MatrixSDK.podspec +++ b/MatrixSDK.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "MatrixSDK" - s.version = "0.17.11" + s.version = "0.18.0" s.summary = "The iOS SDK to build apps compatible with Matrix (https://www.matrix.org)" s.description = <<-DESC diff --git a/MatrixSDK/MatrixSDKVersion.m b/MatrixSDK/MatrixSDKVersion.m index 988712f8ac..1f46ceb748 100644 --- a/MatrixSDK/MatrixSDKVersion.m +++ b/MatrixSDK/MatrixSDKVersion.m @@ -16,4 +16,4 @@ #import -NSString *const MatrixSDKVersion = @"0.17.11"; +NSString *const MatrixSDKVersion = @"0.18.0"; From b64499afd90571912a8f8adb04a3b303a93484ee Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Thu, 11 Feb 2021 14:14:07 +0300 Subject: [PATCH 89/89] finish version++