"Fossies" - the Fresh Open Source Software Archive 
Member "jitsi-meet-7309/ios/sdk/src/callkit/CallKit.m" (31 May 2023, 11980 Bytes) of package /linux/misc/jitsi-meet-7309.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Matlab source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
1 //
2 // Based on RNCallKit
3 //
4 // Original license:
5 //
6 // Copyright (c) 2016, Ian Yu-Hsun Lin
7 //
8 // Permission to use, copy, modify, and/or distribute this software for any
9 // purpose with or without fee is hereby granted, provided that the above
10 // copyright notice and this permission notice appear in all copies.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 //
20
21 #import <AVFoundation/AVFoundation.h>
22 #import <CallKit/CallKit.h>
23 #import <Foundation/Foundation.h>
24 #import <UIKit/UIKit.h>
25
26 #import <React/RCTBridge.h>
27 #import <React/RCTEventEmitter.h>
28 #import <React/RCTUtils.h>
29 #import <React/RCTLog.h>
30 #import <WebRTC/WebRTC.h>
31
32 #import "../JitsiAudioSession.h"
33 #import "JMCallKitProxy.h"
34
35
36 // The events emitted/supported by RNCallKit:
37 static NSString * const RNCallKitPerformAnswerCallAction
38 = @"performAnswerCallAction";
39 static NSString * const RNCallKitPerformEndCallAction
40 = @"performEndCallAction";
41 static NSString * const RNCallKitPerformSetMutedCallAction
42 = @"performSetMutedCallAction";
43 static NSString * const RNCallKitProviderDidReset
44 = @"providerDidReset";
45
46 @interface RNCallKit : RCTEventEmitter <JMCallKitListener>
47 @end
48
49 @implementation RNCallKit
50
51 RCT_EXPORT_MODULE();
52
53 - (NSArray<NSString *> *)supportedEvents {
54 return @[
55 RNCallKitPerformAnswerCallAction,
56 RNCallKitPerformEndCallAction,
57 RNCallKitPerformSetMutedCallAction,
58 RNCallKitProviderDidReset
59 ];
60 }
61
62 - (void)dealloc {
63 [JMCallKitProxy removeListener:self];
64 }
65
66 - (dispatch_queue_t)methodQueue {
67 // Make sure all our methods run in the main thread.
68 return dispatch_get_main_queue();
69 }
70
71 // End call
72 RCT_EXPORT_METHOD(endCall:(NSString *)callUUID
73 resolve:(RCTPromiseResolveBlock)resolve
74 reject:(RCTPromiseRejectBlock)reject) {
75 RCTLogInfo(@"[RNCallKit][endCall] callUUID = %@", callUUID);
76
77 NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
78
79 if (!callUUID_) {
80 reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
81 return;
82 }
83
84 CXEndCallAction *action
85 = [[CXEndCallAction alloc] initWithCallUUID:callUUID_];
86 [self requestTransaction:[[CXTransaction alloc] initWithAction:action]
87 resolve:resolve
88 reject:reject];
89 }
90
91 // Mute / unmute (audio)
92 RCT_EXPORT_METHOD(setMuted:(NSString *)callUUID
93 muted:(BOOL)muted
94 resolve:(RCTPromiseResolveBlock)resolve
95 reject:(RCTPromiseRejectBlock)reject) {
96 RCTLogInfo(@"[RNCallKit][setMuted] callUUID = %@", callUUID);
97
98 NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
99
100 if (!callUUID_) {
101 reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
102 return;
103 }
104
105 CXSetMutedCallAction *action
106 = [[CXSetMutedCallAction alloc] initWithCallUUID:callUUID_ muted:muted];
107 [self requestTransaction:[[CXTransaction alloc] initWithAction:action]
108 resolve:resolve
109 reject:reject];
110 }
111
112 RCT_EXPORT_METHOD(setProviderConfiguration:(NSDictionary *)dictionary) {
113 RCTLogInfo(@"[RNCallKit][setProviderConfiguration:] dictionary = %@", dictionary);
114
115 if (![JMCallKitProxy isProviderConfigured]) {
116 JMCallKitProxy.enabled = true;
117 [self configureProviderFromDictionary:dictionary];
118 }
119
120 // register to receive CallKit proxy events
121 [JMCallKitProxy addListener:self];
122 }
123
124 // Start outgoing call
125 RCT_EXPORT_METHOD(startCall:(NSString *)callUUID
126 handle:(NSString *)handle
127 video:(BOOL)video
128 resolve:(RCTPromiseResolveBlock)resolve
129 reject:(RCTPromiseRejectBlock)reject) {
130 RCTLogInfo(@"[RNCallKit][startCall] callUUID = %@", callUUID);
131
132 NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
133
134 if (!callUUID_) {
135 reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
136 return;
137 }
138
139 // Don't start a new call if there's an active call for the specified
140 // callUUID. JitsiMeetView was configured for an incoming call.
141 if ([JMCallKitProxy hasActiveCallForUUID:callUUID]) {
142 resolve(nil);
143 return;
144 }
145
146 CXHandle *handle_
147 = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
148 CXStartCallAction *action
149 = [[CXStartCallAction alloc] initWithCallUUID:callUUID_
150 handle:handle_];
151 action.video = video;
152 CXTransaction *transaction = [[CXTransaction alloc] initWithAction:action];
153 [self requestTransaction:transaction resolve:resolve reject:reject];
154 }
155
156 // Indicate call failed
157 RCT_EXPORT_METHOD(reportCallFailed:(NSString *)callUUID
158 resolve:(RCTPromiseResolveBlock)resolve
159 reject:(RCTPromiseRejectBlock)reject) {
160 NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
161
162 if (!callUUID_) {
163 reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
164 return;
165 }
166
167 [JMCallKitProxy reportCallWith:callUUID_
168 endedAt:nil
169 reason:CXCallEndedReasonFailed];
170 resolve(nil);
171 }
172
173 // Indicate outgoing call connected.
174 RCT_EXPORT_METHOD(reportConnectedOutgoingCall:(NSString *)callUUID
175 resolve:(RCTPromiseResolveBlock)resolve
176 reject:(RCTPromiseRejectBlock)reject) {
177 NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
178
179 if (!callUUID_) {
180 reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
181 return;
182 }
183
184 [JMCallKitProxy reportOutgoingCallWith:callUUID_
185 connectedAt:nil];
186 resolve(nil);
187 }
188
189 // Update call in case we have a display name or video capability changes.
190 RCT_EXPORT_METHOD(updateCall:(NSString *)callUUID
191 options:(NSDictionary *)options
192 resolve:(RCTPromiseResolveBlock)resolve
193 reject:(RCTPromiseRejectBlock)reject) {
194 RCTLogInfo(@"[RNCallKit][updateCall] callUUID = %@ options = %@", callUUID, options);
195
196 NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
197
198 if (!callUUID_) {
199 reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
200 return;
201 }
202
203 NSString *displayName = options[@"displayName"];
204 BOOL hasVideo = [(NSNumber*)options[@"hasVideo"] boolValue];
205
206 [JMCallKitProxy reportCallUpdateWith:callUUID_
207 handle:nil
208 displayName:displayName
209 hasVideo:hasVideo];
210
211 resolve(nil);
212 }
213
214 #pragma mark - Helper methods
215
216 - (void)configureProviderFromDictionary:(NSDictionary* )dictionary {
217 RCTLogInfo(@"[RNCallKit][providerConfigurationFromDictionary: %@]", dictionary);
218
219 if (!dictionary) {
220 dictionary = @{};
221 }
222
223 // localizedName
224 NSString *localizedName = dictionary[@"localizedName"];
225 if (!localizedName) {
226 localizedName
227 = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"];
228 }
229
230 // iconTemplateImageData
231 NSString *iconTemplateImageName = dictionary[@"iconTemplateImageName"];
232 NSData *iconTemplateImageData;
233 UIImage *iconTemplateImage;
234 if (iconTemplateImageName) {
235 // First try to load the resource from the main bundle.
236 iconTemplateImage = [UIImage imageNamed:iconTemplateImageName];
237
238 // If that didn't work, use the one built-in.
239 if (!iconTemplateImage) {
240 iconTemplateImage = [UIImage imageNamed:iconTemplateImageName
241 inBundle:[NSBundle bundleForClass:self.class]
242 compatibleWithTraitCollection:nil];
243 }
244
245 if (iconTemplateImage) {
246 iconTemplateImageData = UIImagePNGRepresentation(iconTemplateImage);
247 }
248 }
249
250 NSString *ringtoneSound = dictionary[@"ringtoneSound"];
251
252 [JMCallKitProxy
253 configureProviderWithLocalizedName:localizedName
254 ringtoneSound:ringtoneSound
255 iconTemplateImageData:iconTemplateImageData];
256 }
257
258 - (void)requestTransaction:(CXTransaction *)transaction
259 resolve:(RCTPromiseResolveBlock)resolve
260 reject:(RCTPromiseRejectBlock)reject {
261 RCTLogInfo(@"[RNCallKit][requestTransaction] transaction = %@", transaction);
262
263 [JMCallKitProxy request:transaction
264 completion:^(NSError * _Nullable error) {
265 if (error) {
266 RCTLogError(@"[RNCallKit][requestTransaction] Error requesting transaction (%@): (%@)", transaction.actions, error);
267 reject(nil, @"Error processing CallKit transaction", error);
268 } else {
269 resolve(nil);
270 }
271 }];
272 }
273
274 #pragma mark - JMCallKitListener
275
276 // Called when the provider has been reset. We should terminate all calls.
277 - (void)providerDidReset {
278 RCTLogInfo(@"[RNCallKit][CXProviderDelegate][providerDidReset:]");
279
280 [self sendEventWithName:RNCallKitProviderDidReset body:nil];
281 }
282
283 // Answering incoming call
284 - (void) performAnswerCallWithUUID:(NSUUID *)UUID {
285 RCTLogInfo(@"[RNCallKit][CXProviderDelegate][provider:performAnswerCallAction:]");
286
287 [self sendEventWithName:RNCallKitPerformAnswerCallAction
288 body:@{ @"callUUID": UUID.UUIDString }];
289 }
290
291 // Call ended, user request
292 - (void) performEndCallWithUUID:(NSUUID *)UUID {
293 RCTLogInfo(@"[RNCallKit][CXProviderDelegate][provider:performEndCallAction:]");
294
295 [self sendEventWithName:RNCallKitPerformEndCallAction
296 body:@{ @"callUUID": UUID.UUIDString }];
297 }
298
299 // Handle audio mute from CallKit view
300 - (void) performSetMutedCallWithUUID:(NSUUID *)UUID
301 isMuted:(BOOL)isMuted {
302 RCTLogInfo(@"[RNCallKit][CXProviderDelegate][provider:performSetMutedCallAction:]");
303
304 [self sendEventWithName:RNCallKitPerformSetMutedCallAction
305 body:@{
306 @"callUUID": UUID.UUIDString,
307 @"muted": @(isMuted)
308 }];
309 }
310
311 // Starting outgoing call
312 - (void) performStartCallWithUUID:(NSUUID *)UUID
313 isVideo:(BOOL)isVideo {
314 RCTLogInfo(@"[RNCallKit][CXProviderDelegate][provider:performStartCallAction:]");
315
316 [JMCallKitProxy reportOutgoingCallWith:UUID
317 startedConnectingAt:nil];
318 }
319
320 - (void) providerDidActivateAudioSessionWithSession:(AVAudioSession *)session {
321 RCTLogInfo(@"[RNCallKit][CXProviderDelegate][provider:didActivateAudioSession:]");
322
323 [JitsiAudioSession activateWithAudioSession:session];
324 }
325
326 - (void) providerDidDeactivateAudioSessionWithSession:(AVAudioSession *)session {
327 RCTLogInfo(@"[RNCallKit][CXProviderDelegate][provider:didDeactivateAudioSession:]");
328
329 [JitsiAudioSession deactivateWithAudioSession:session];
330 }
331
332 - (void) providerTimedOutPerformingActionWithAction:(CXAction *)action {
333 RCTLogWarn(@"[RNCallKit][CXProviderDelegate][provider:timedOutPerformingAction:]");
334 }
335
336
337 // The bridge might already be invalidated by the time a CallKit event is processed,
338 // just ignore it and don't emit it.
339 - (void)sendEventWithName:(NSString *)name body:(id)body {
340 if (!self.bridge) {
341 return;
342 }
343
344 [super sendEventWithName:name body:body];
345 }
346
347 @end