"Fossies" - the Fresh Open Source Software Archive

Member "jitsi-meet-6193/ios/sdk/src/callkit/CallKit.m" (20 May 2022, 11978 Bytes) of package /linux/misc/jitsi-meet-6193.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 <WebRTC/WebRTC.h>
   30 
   31 #import <JitsiMeetSDK/JitsiMeetSDK-Swift.h>
   32 
   33 #import "../LogUtils.h"
   34 #import "JitsiAudioSession.h"
   35 
   36 
   37 // The events emitted/supported by RNCallKit:
   38 static NSString * const RNCallKitPerformAnswerCallAction
   39     = @"performAnswerCallAction";
   40 static NSString * const RNCallKitPerformEndCallAction
   41     = @"performEndCallAction";
   42 static NSString * const RNCallKitPerformSetMutedCallAction
   43     = @"performSetMutedCallAction";
   44 static NSString * const RNCallKitProviderDidReset
   45     = @"providerDidReset";
   46 
   47 @interface RNCallKit : RCTEventEmitter <JMCallKitListener>
   48 @end
   49 
   50 @implementation RNCallKit
   51 
   52 RCT_EXPORT_MODULE();
   53 
   54 - (NSArray<NSString *> *)supportedEvents {
   55     return @[
   56         RNCallKitPerformAnswerCallAction,
   57         RNCallKitPerformEndCallAction,
   58         RNCallKitPerformSetMutedCallAction,
   59         RNCallKitProviderDidReset
   60     ];
   61 }
   62 
   63 - (void)dealloc {
   64     [JMCallKitProxy removeListener:self];
   65 }
   66 
   67 - (dispatch_queue_t)methodQueue {
   68     // Make sure all our methods run in the main thread.
   69     return dispatch_get_main_queue();
   70 }
   71 
   72 // End call
   73 RCT_EXPORT_METHOD(endCall:(NSString *)callUUID
   74                   resolve:(RCTPromiseResolveBlock)resolve
   75                    reject:(RCTPromiseRejectBlock)reject) {
   76     DDLogInfo(@"[RNCallKit][endCall] callUUID = %@", callUUID);
   77 
   78     NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
   79 
   80     if (!callUUID_) {
   81         reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
   82         return;
   83     }
   84 
   85     CXEndCallAction *action
   86         = [[CXEndCallAction alloc] initWithCallUUID:callUUID_];
   87     [self requestTransaction:[[CXTransaction alloc] initWithAction:action]
   88                      resolve:resolve
   89                       reject:reject];
   90 }
   91 
   92 // Mute / unmute (audio)
   93 RCT_EXPORT_METHOD(setMuted:(NSString *)callUUID
   94                      muted:(BOOL)muted
   95                    resolve:(RCTPromiseResolveBlock)resolve
   96                     reject:(RCTPromiseRejectBlock)reject) {
   97     DDLogInfo(@"[RNCallKit][setMuted] callUUID = %@", callUUID);
   98 
   99     NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
  100 
  101     if (!callUUID_) {
  102         reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
  103         return;
  104     }
  105 
  106     CXSetMutedCallAction *action
  107         = [[CXSetMutedCallAction alloc] initWithCallUUID:callUUID_ muted:muted];
  108     [self requestTransaction:[[CXTransaction alloc] initWithAction:action]
  109                      resolve:resolve
  110                       reject:reject];
  111 }
  112 
  113 RCT_EXPORT_METHOD(setProviderConfiguration:(NSDictionary *)dictionary) {
  114     DDLogInfo(@"[RNCallKit][setProviderConfiguration:] dictionary = %@", dictionary);
  115 
  116     if (![JMCallKitProxy isProviderConfigured]) {
  117         JMCallKitProxy.enabled = true;
  118         [self configureProviderFromDictionary:dictionary];
  119     }
  120 
  121     // register to receive CallKit proxy events
  122     [JMCallKitProxy addListener:self];
  123 }
  124 
  125 // Start outgoing call
  126 RCT_EXPORT_METHOD(startCall:(NSString *)callUUID
  127                      handle:(NSString *)handle
  128                       video:(BOOL)video
  129                     resolve:(RCTPromiseResolveBlock)resolve
  130                      reject:(RCTPromiseRejectBlock)reject) {
  131     DDLogInfo(@"[RNCallKit][startCall] callUUID = %@", callUUID);
  132 
  133     NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
  134 
  135     if (!callUUID_) {
  136         reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
  137         return;
  138     }
  139 
  140     // Don't start a new call if there's an active call for the specified
  141     // callUUID. JitsiMeetView was configured for an incoming call.
  142     if ([JMCallKitProxy hasActiveCallForUUID:callUUID]) {
  143         resolve(nil);
  144         return;
  145     }
  146 
  147     CXHandle *handle_
  148         = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
  149     CXStartCallAction *action
  150         = [[CXStartCallAction alloc] initWithCallUUID:callUUID_
  151                                                handle:handle_];
  152     action.video = video;
  153     CXTransaction *transaction = [[CXTransaction alloc] initWithAction:action];
  154     [self requestTransaction:transaction resolve:resolve reject:reject];
  155 }
  156 
  157 // Indicate call failed
  158 RCT_EXPORT_METHOD(reportCallFailed:(NSString *)callUUID
  159                            resolve:(RCTPromiseResolveBlock)resolve
  160                             reject:(RCTPromiseRejectBlock)reject) {
  161     NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
  162 
  163     if (!callUUID_) {
  164         reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
  165         return;
  166     }
  167 
  168     [JMCallKitProxy reportCallWith:callUUID_
  169                            endedAt:nil
  170                             reason:CXCallEndedReasonFailed];
  171     resolve(nil);
  172 }
  173 
  174 // Indicate outgoing call connected.
  175 RCT_EXPORT_METHOD(reportConnectedOutgoingCall:(NSString *)callUUID
  176                                       resolve:(RCTPromiseResolveBlock)resolve
  177                                        reject:(RCTPromiseRejectBlock)reject) {
  178     NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
  179 
  180     if (!callUUID_) {
  181         reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
  182         return;
  183     }
  184 
  185     [JMCallKitProxy reportOutgoingCallWith:callUUID_
  186                                connectedAt:nil];
  187     resolve(nil);
  188 }
  189 
  190 // Update call in case we have a display name or video capability changes.
  191 RCT_EXPORT_METHOD(updateCall:(NSString *)callUUID
  192                      options:(NSDictionary *)options
  193                      resolve:(RCTPromiseResolveBlock)resolve
  194                       reject:(RCTPromiseRejectBlock)reject) {
  195     DDLogInfo(@"[RNCallKit][updateCall] callUUID = %@ options = %@", callUUID, options);
  196 
  197     NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
  198 
  199     if (!callUUID_) {
  200         reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
  201         return;
  202     }
  203 
  204     NSString *displayName = options[@"displayName"];
  205     BOOL hasVideo = [(NSNumber*)options[@"hasVideo"] boolValue];
  206 
  207     [JMCallKitProxy reportCallUpdateWith:callUUID_
  208                                   handle:nil
  209                              displayName:displayName
  210                                 hasVideo:hasVideo];
  211 
  212     resolve(nil);
  213 }
  214 
  215 #pragma mark - Helper methods
  216 
  217 - (void)configureProviderFromDictionary:(NSDictionary* )dictionary {
  218     DDLogInfo(@"[RNCallKit][providerConfigurationFromDictionary: %@]", dictionary);
  219 
  220     if (!dictionary) {
  221         dictionary = @{};
  222     }
  223 
  224     // localizedName
  225     NSString *localizedName = dictionary[@"localizedName"];
  226     if (!localizedName) {
  227         localizedName
  228             = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"];
  229     }
  230 
  231     // iconTemplateImageData
  232     NSString *iconTemplateImageName = dictionary[@"iconTemplateImageName"];
  233     NSData *iconTemplateImageData;
  234     UIImage *iconTemplateImage;
  235     if (iconTemplateImageName) {
  236         // First try to load the resource from the main bundle.
  237         iconTemplateImage = [UIImage imageNamed:iconTemplateImageName];
  238 
  239         // If that didn't work, use the one built-in.
  240         if (!iconTemplateImage) {
  241             iconTemplateImage = [UIImage imageNamed:iconTemplateImageName
  242                                            inBundle:[NSBundle bundleForClass:self.class]
  243                       compatibleWithTraitCollection:nil];
  244         }
  245 
  246         if (iconTemplateImage) {
  247             iconTemplateImageData = UIImagePNGRepresentation(iconTemplateImage);
  248         }
  249     }
  250 
  251     NSString *ringtoneSound = dictionary[@"ringtoneSound"];
  252 
  253     [JMCallKitProxy
  254         configureProviderWithLocalizedName:localizedName
  255                              ringtoneSound:ringtoneSound
  256                      iconTemplateImageData:iconTemplateImageData];
  257 }
  258 
  259 - (void)requestTransaction:(CXTransaction *)transaction
  260                    resolve:(RCTPromiseResolveBlock)resolve
  261                     reject:(RCTPromiseRejectBlock)reject {
  262     DDLogInfo(@"[RNCallKit][requestTransaction] transaction = %@", transaction);
  263 
  264     [JMCallKitProxy request:transaction
  265                  completion:^(NSError * _Nullable error) {
  266         if (error) {
  267             DDLogError(@"[RNCallKit][requestTransaction] Error requesting transaction (%@): (%@)", transaction.actions, error);
  268             reject(nil, @"Error processing CallKit transaction", error);
  269         } else {
  270             resolve(nil);
  271         }
  272     }];
  273 }
  274 
  275 #pragma mark - JMCallKitListener
  276 
  277 // Called when the provider has been reset. We should terminate all calls.
  278 - (void)providerDidReset {
  279     DDLogInfo(@"[RNCallKit][CXProviderDelegate][providerDidReset:]");
  280 
  281     [self sendEventWithName:RNCallKitProviderDidReset body:nil];
  282 }
  283 
  284 // Answering incoming call
  285 - (void) performAnswerCallWithUUID:(NSUUID *)UUID {
  286     DDLogInfo(@"[RNCallKit][CXProviderDelegate][provider:performAnswerCallAction:]");
  287 
  288     [self sendEventWithName:RNCallKitPerformAnswerCallAction
  289                        body:@{ @"callUUID": UUID.UUIDString }];
  290 }
  291 
  292 // Call ended, user request
  293 - (void) performEndCallWithUUID:(NSUUID *)UUID {
  294     DDLogInfo(@"[RNCallKit][CXProviderDelegate][provider:performEndCallAction:]");
  295 
  296     [self sendEventWithName:RNCallKitPerformEndCallAction
  297                        body:@{ @"callUUID": UUID.UUIDString }];
  298 }
  299 
  300 // Handle audio mute from CallKit view
  301 - (void) performSetMutedCallWithUUID:(NSUUID *)UUID
  302                              isMuted:(BOOL)isMuted {
  303     DDLogInfo(@"[RNCallKit][CXProviderDelegate][provider:performSetMutedCallAction:]");
  304 
  305     [self sendEventWithName:RNCallKitPerformSetMutedCallAction
  306                        body:@{
  307                            @"callUUID": UUID.UUIDString,
  308                            @"muted": @(isMuted)
  309                        }];
  310 }
  311 
  312 // Starting outgoing call
  313 - (void) performStartCallWithUUID:(NSUUID *)UUID
  314                           isVideo:(BOOL)isVideo {
  315     DDLogInfo(@"[RNCallKit][CXProviderDelegate][provider:performStartCallAction:]");
  316 
  317     [JMCallKitProxy reportOutgoingCallWith:UUID
  318                        startedConnectingAt:nil];
  319 }
  320 
  321 - (void) providerDidActivateAudioSessionWithSession:(AVAudioSession *)session {
  322     DDLogInfo(@"[RNCallKit][CXProviderDelegate][provider:didActivateAudioSession:]");
  323 
  324     [JitsiAudioSession activateWithAudioSession:session];
  325 }
  326 
  327 - (void) providerDidDeactivateAudioSessionWithSession:(AVAudioSession *)session {
  328     DDLogInfo(@"[RNCallKit][CXProviderDelegate][provider:didDeactivateAudioSession:]");
  329 
  330     [JitsiAudioSession deactivateWithAudioSession:session];
  331 }
  332 
  333 - (void) providerTimedOutPerformingActionWithAction:(CXAction *)action {
  334     DDLogWarn(@"[RNCallKit][CXProviderDelegate][provider:timedOutPerformingAction:]");
  335 }
  336 
  337 
  338 // The bridge might already be invalidated by the time a CallKit event is processed,
  339 // just ignore it and don't emit it.
  340 - (void)sendEventWithName:(NSString *)name body:(id)body {
  341     if (!self.bridge) {
  342         return;
  343     }
  344 
  345     [super sendEventWithName:name body:body];
  346 }
  347 
  348 @end