#import "RCTBridgeModule.h" #import "RCTLog.h" #import @interface AudioMode : NSObject @end @implementation AudioMode { NSString *_category; NSString *_mode; } RCT_EXPORT_MODULE(); typedef enum { kAudioModeDefault, kAudioModeAudioCall, kAudioModeVideoCall } JitsiMeetAudioMode; - (NSDictionary *)constantsToExport { return @{ @"AUDIO_CALL" : [NSNumber numberWithInt: kAudioModeAudioCall], @"DEFAULT" : [NSNumber numberWithInt: kAudioModeDefault], @"VIDEO_CALL" : [NSNumber numberWithInt: kAudioModeVideoCall] }; }; - (instancetype)init { self = [super init]; if (self) { _category = nil; _mode = nil; } return self; } - (dispatch_queue_t)methodQueue { // Make sure all our methods run in the main thread. The route change // notification runs there so this will make sure it will only be fired // after our changes have been applied (when we cause them, that is). return dispatch_get_main_queue(); } - (void)routeChanged:(NSNotification*)notification { NSInteger reason = [[notification.userInfo valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; switch (reason) { case AVAudioSessionRouteChangeReasonCategoryChange: // The category has changed. Check if it's the one we want and adjust as // needed. [self setCategory:_category mode:_mode error:nil]; break; default: // Do nothing. break; } } - (BOOL)setCategory:(NSString *)category mode:(NSString *)mode error:(NSError * _Nullable *)outError { AVAudioSession *session = [AVAudioSession sharedInstance]; if (session.category != category && ![session setCategory:category error:outError]) { RCTLogError(@"Failed to (re)apply specified AVAudioSession category!"); return NO; } if (session.mode != mode && ![session setMode:mode error:outError]) { RCTLogError(@"Failed to (re)apply specified AVAudioSession mode!"); return NO; } return YES; } RCT_EXPORT_METHOD(setMode:(int)mode resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { NSString *avCategory; NSString *avMode; NSError *error; switch (mode) { case kAudioModeAudioCall: avCategory = AVAudioSessionCategoryPlayAndRecord; avMode = AVAudioSessionModeVoiceChat; break; case kAudioModeDefault: avCategory = AVAudioSessionCategorySoloAmbient; avMode = AVAudioSessionModeDefault; break; case kAudioModeVideoCall: avCategory = AVAudioSessionCategoryPlayAndRecord; avMode = AVAudioSessionModeVideoChat; break; default: reject(@"setMode", @"Invalid mode", nil); return; } if (![self setCategory:avCategory mode:avMode error:&error] || error) { reject(@"setMode", error.localizedDescription, error); return; } // Even though the specified category and mode were successfully set, the // AVAudioSession is a singleton and other parts of the application such as // WebRTC may undo the settings. Make sure that the settings are reapplied // upon undoes. if (!_category || !_mode) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChanged:) name:AVAudioSessionRouteChangeNotification object:nil]; } // Save the desired/specified category and mode so that they may be // reapplied (upon undoes as described above). _category = avCategory; _mode = avMode; resolve(nil); } @end