"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