@cloudflare/realtimekit-react-native 0.0.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +83 -0
  3. package/RealtimeKitCore.podspec +19 -0
  4. package/android/build.gradle +58 -0
  5. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  6. package/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  7. package/android/gradlew +185 -0
  8. package/android/gradlew.bat +89 -0
  9. package/android/src/main/AndroidManifest.xml +23 -0
  10. package/android/src/main/java/com/reactnativecore/BackgroundTimer/RNBackgroundTimer.java +97 -0
  11. package/android/src/main/java/com/reactnativecore/Constants.java +14 -0
  12. package/android/src/main/java/com/reactnativecore/CoreModule.java +49 -0
  13. package/android/src/main/java/com/reactnativecore/CorePackage.java +34 -0
  14. package/android/src/main/java/com/reactnativecore/DyteHelperModule.java +268 -0
  15. package/android/src/main/java/com/reactnativecore/DyteHolder.java +7 -0
  16. package/android/src/main/java/com/reactnativecore/ForegroundService.java +48 -0
  17. package/android/src/main/java/com/reactnativecore/NotificationHelper.java +127 -0
  18. package/android/src/main/java/com/reactnativecore/RNPermissionsModule.java +226 -0
  19. package/android/src/main/java/com/reactnativecore/incallmanager/AppRTCBluetoothManager.java +637 -0
  20. package/android/src/main/java/com/reactnativecore/incallmanager/AppRTCProximitySensor.java +133 -0
  21. package/android/src/main/java/com/reactnativecore/incallmanager/AppRTCUtils.java +41 -0
  22. package/android/src/main/java/com/reactnativecore/incallmanager/InCallManagerModule.java +1881 -0
  23. package/android/src/main/java/com/reactnativecore/incallmanager/InCallProximityManager.java +162 -0
  24. package/android/src/main/java/com/reactnativecore/incallmanager/InCallWakeLockUtils.java +116 -0
  25. package/android/src/main/java/com/reactnativecore/incallmanager/ThreadUtils.java +39 -0
  26. package/app.plugin.js +1 -0
  27. package/ios/BroadcastEventEmitter.h +11 -0
  28. package/ios/BroadcastEventEmitter.m +72 -0
  29. package/ios/Core.h +5 -0
  30. package/ios/Core.m +28 -0
  31. package/ios/Core.xcodeproj/project.pbxproj +287 -0
  32. package/ios/DyteRNBackgroundTimer.h +22 -0
  33. package/ios/DyteRNBackgroundTimer.m +90 -0
  34. package/ios/DyteRNPermissionHandlerNotifications.h +17 -0
  35. package/ios/DyteRNPermissionHandlerNotifications.m +127 -0
  36. package/ios/DyteRNPermissions.h +50 -0
  37. package/ios/DyteRNPermissions.m +275 -0
  38. package/ios/DyteScreensharePickerView.h +8 -0
  39. package/ios/DyteScreensharePickerView.m +27 -0
  40. package/ios/PrivacyInfo.xcprivacy +60 -0
  41. package/ios/RNInCallManager.h +16 -0
  42. package/ios/RNInCallManager.m +1354 -0
  43. package/ios/permissions/DyteRNPermissionHandlerBluetoothPeripheral.h +4 -0
  44. package/ios/permissions/DyteRNPermissionHandlerBluetoothPeripheral.m +87 -0
  45. package/ios/permissions/DyteRNPermissionHandlerCamera.h +5 -0
  46. package/ios/permissions/DyteRNPermissionHandlerCamera.m +38 -0
  47. package/ios/permissions/DyteRNPermissionHandlerMicrophone.h +5 -0
  48. package/ios/permissions/DyteRNPermissionHandlerMicrophone.m +34 -0
  49. package/ios/permissions/DyteRNPermissionHandlerPhotoLibrary.h +8 -0
  50. package/ios/permissions/DyteRNPermissionHandlerPhotoLibrary.m +70 -0
  51. package/ios/screenshare/DyteDarwinNotificationCenter.swift +21 -0
  52. package/ios/screenshare/DyteScreenshareAtomic.swift +29 -0
  53. package/ios/screenshare/DyteScreenshareHandler.swift +103 -0
  54. package/ios/screenshare/DyteScreenshareUploader.swift +139 -0
  55. package/ios/screenshare/DyteSocketConnection.swift +190 -0
  56. package/lib/BackgroundHandler.d.ts +20 -0
  57. package/lib/BackgroundHandler.js +87 -0
  58. package/lib/LocalMediaError.d.ts +4 -0
  59. package/lib/LocalMediaError.js +6 -0
  60. package/lib/LocalMediaHandler.d.ts +118 -0
  61. package/lib/LocalMediaHandler.js +853 -0
  62. package/lib/LocalMediaInterfaces.d.ts +76 -0
  63. package/lib/LocalMediaInterfaces.js +1 -0
  64. package/lib/LocalMediaUtils.d.ts +93 -0
  65. package/lib/LocalMediaUtils.js +276 -0
  66. package/lib/NativeAudioManager.d.ts +16 -0
  67. package/lib/NativeAudioManager.js +93 -0
  68. package/lib/PermissionHandler.d.ts +34 -0
  69. package/lib/PermissionHandler.js +179 -0
  70. package/lib/ReactContext.d.ts +42 -0
  71. package/lib/ReactContext.js +96 -0
  72. package/lib/ReactHooks.d.ts +11 -0
  73. package/lib/ReactHooks.js +73 -0
  74. package/lib/index.d.ts +10 -0
  75. package/lib/index.js +40 -0
  76. package/lib/utils/crypto.d.ts +17 -0
  77. package/lib/utils/crypto.js +82 -0
  78. package/package.json +51 -4
  79. package/plugin/build/withDyte.d.ts +25 -0
  80. package/plugin/build/withDyte.js +100 -0
  81. package/plugin/src/withDyte.ts +150 -0
  82. package/plugin/tsconfig.json +10 -0
  83. package/plugin/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,1354 @@
1
+ #import "RNInCallManager.h"
2
+ #import <React/RCTBridge.h>
3
+ #import <React/RCTConvert.h>
4
+ #import <React/RCTEventDispatcher.h>
5
+ #import <React/RCTUtils.h>
6
+
7
+ //static BOOL const automatic = YES;
8
+
9
+ @implementation RNInCallManager
10
+
11
+ {
12
+
13
+ UIDevice *_currentDevice;
14
+
15
+ AVAudioSession *_audioSession;
16
+ AVAudioPlayer *_ringtone;
17
+ AVAudioPlayer *_ringback;
18
+ AVAudioPlayer *_busytone;
19
+
20
+ NSURL *_defaultRingtoneUri;
21
+ NSURL *_defaultRingbackUri;
22
+ NSURL *_defaultBusytoneUri;
23
+ NSURL *_bundleRingtoneUri;
24
+ NSURL *_bundleRingbackUri;
25
+ NSURL *_bundleBusytoneUri;
26
+
27
+ //BOOL isProximitySupported;
28
+ BOOL _proximityIsNear;
29
+
30
+ // --- tags to indicating which observer has added
31
+ BOOL _isProximityRegistered;
32
+ BOOL _isAudioSessionInterruptionRegistered;
33
+ BOOL _isAudioSessionRouteChangeRegistered;
34
+ BOOL _isAudioSessionMediaServicesWereLostRegistered;
35
+ BOOL _isAudioSessionMediaServicesWereResetRegistered;
36
+ BOOL _isAudioSessionSilenceSecondaryAudioHintRegistered;
37
+
38
+ // -- notification observers
39
+ id _proximityObserver;
40
+ id _audioSessionInterruptionObserver;
41
+ id _audioSessionRouteChangeObserver;
42
+ id _audioSessionMediaServicesWereLostObserver;
43
+ id _audioSessionMediaServicesWereResetObserver;
44
+ id _audioSessionSilenceSecondaryAudioHintObserver;
45
+
46
+ NSString *_incallAudioMode;
47
+ NSString *_incallAudioCategory;
48
+ NSString *_origAudioCategory;
49
+ NSString *_origAudioMode;
50
+ NSString *_userSelectedAudioRoute;
51
+ NSString *_currentAudioRoute;
52
+ BOOL _audioSessionInitialized;
53
+ int _forceSpeakerOn;
54
+ int _turnBluetoothOn;
55
+ NSString *_media;
56
+ }
57
+
58
+ + (BOOL)requiresMainQueueSetup
59
+ {
60
+ return NO;
61
+ }
62
+
63
+ RCT_EXPORT_MODULE(InCallManager)
64
+
65
+ - (instancetype)init
66
+ {
67
+ if (self = [super init]) {
68
+ _currentDevice = [UIDevice currentDevice];
69
+ _audioSession = [AVAudioSession sharedInstance];
70
+ _ringtone = nil;
71
+ _ringback = nil;
72
+ _busytone = nil;
73
+
74
+ _defaultRingtoneUri = nil;
75
+ _defaultRingbackUri = nil;
76
+ _defaultBusytoneUri = nil;
77
+ _bundleRingtoneUri = nil;
78
+ _bundleRingbackUri = nil;
79
+ _bundleBusytoneUri = nil;
80
+
81
+ _proximityIsNear = NO;
82
+
83
+ _isProximityRegistered = NO;
84
+ _isAudioSessionInterruptionRegistered = NO;
85
+ _isAudioSessionRouteChangeRegistered = NO;
86
+ _isAudioSessionMediaServicesWereLostRegistered = NO;
87
+ _isAudioSessionMediaServicesWereResetRegistered = NO;
88
+ _isAudioSessionSilenceSecondaryAudioHintRegistered = NO;
89
+
90
+ _proximityObserver = nil;
91
+ _audioSessionInterruptionObserver = nil;
92
+ _audioSessionRouteChangeObserver = nil;
93
+ _audioSessionMediaServicesWereLostObserver = nil;
94
+ _audioSessionMediaServicesWereResetObserver = nil;
95
+ _audioSessionSilenceSecondaryAudioHintObserver = nil;
96
+
97
+ _incallAudioMode = AVAudioSessionModeVoiceChat;
98
+ _incallAudioCategory = AVAudioSessionCategoryPlayAndRecord;
99
+ _origAudioCategory = nil;
100
+ _origAudioMode = nil;
101
+ _audioSessionInitialized = NO;
102
+ _forceSpeakerOn = 0;
103
+ _media = @"audio";
104
+
105
+ // NSLog(@"RNInCallManager.init(): initialized");
106
+ }
107
+ return self;
108
+ }
109
+
110
+ - (void)dealloc
111
+ {
112
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
113
+ [self stop:@""];
114
+ }
115
+
116
+ //Supported events
117
+ - (NSArray<NSString *> *)supportedEvents
118
+ {
119
+ return @[@"Proximity",
120
+ @"WiredHeadset",
121
+ @"onAudioDeviceChanged",
122
+ @"Bluetooth"];
123
+ }
124
+
125
+ RCT_EXPORT_METHOD(start:(NSString *)mediaType
126
+ auto:(BOOL)_auto
127
+ ringbackUriType:(NSString *)ringbackUriType)
128
+ {
129
+ // NSLog(@"🤟 Starting InCall manager 🤟 ");
130
+ if (_audioSessionInitialized) {
131
+ return;
132
+ }
133
+ _media = mediaType;
134
+
135
+ // --- auto is always true on ios
136
+ // if ([_media isEqualToString:@"video"]) {
137
+ // _incallAudioMode = AVAudioSessionModeVideoChat;
138
+ // } else {
139
+ // _incallAudioMode = AVAudioSessionModeVoiceChat;
140
+ // }
141
+ _incallAudioMode = AVAudioSessionModeVoiceChat;
142
+ // NSLog(@"🧑‍💻 RNInCallManager.start() start InCallManager. media=%@, type=%@, mode=%@", _media, _media, _incallAudioMode);
143
+ [self storeOriginalAudioSetup];
144
+ _forceSpeakerOn = 0;
145
+ [self startAudioSessionNotification];
146
+ [self audioSessionSetCategory:_incallAudioCategory
147
+ options:0
148
+ callerMemo:NSStringFromSelector(_cmd)];
149
+ [self audioSessionSetMode:_incallAudioMode
150
+ callerMemo:NSStringFromSelector(_cmd)];
151
+ [self audioSessionSetActive:YES
152
+ options:0
153
+ callerMemo:NSStringFromSelector(_cmd)];
154
+
155
+ if (ringbackUriType.length > 0) {
156
+ // NSLog(@"RNInCallManager.start() play ringback first. type=%@", ringbackUriType);
157
+ [self startRingback:ringbackUriType];
158
+ }
159
+
160
+ // if ([_media isEqualToString:@"audio"]) {
161
+ // [self startProximitySensor];
162
+ // }
163
+ [self setKeepScreenOn:YES];
164
+ _audioSessionInitialized = YES;
165
+ //self.debugAudioSession()
166
+ // NSLog(@"🤟 Audio Session Initalized 🤟 ");
167
+ }
168
+
169
+ RCT_EXPORT_METHOD(chooseAudioRoute: (NSString *)audioRoute Promise:(RCTPromiseResolveBlock)resolve
170
+ reject:(RCTPromiseRejectBlock)reject) {
171
+ // NSLog(@"💂‍♂️💂‍♂️ choseAudio Route Called");
172
+ // NSLog(@"👨‍💻 Selected Audio Route: %@", audioRoute);
173
+ BOOL success;
174
+ // SPEAKER_PHONE,
175
+ // WIRED_HEADSET,
176
+ // EARPIECE,
177
+ // BLUETOOTH,
178
+ // NONE
179
+ // TODO: Create enums for AudioRoute
180
+ _userSelectedAudioRoute = audioRoute;
181
+ if (![_userSelectedAudioRoute isEqualToString:_currentAudioRoute]) {
182
+ if ([audioRoute isEqual: @"SPEAKER_PHONE"] || [audioRoute isEqualToString:@"speaker"]) {
183
+ _forceSpeakerOn = 1;
184
+ [self updateAudioRoute];
185
+ success = true;
186
+ } else if ([audioRoute isEqualToString:@"WIRED_HEADSET"] || [audioRoute isEqualToString:@"wired"]) {
187
+ _forceSpeakerOn = 0;
188
+ success = [self routeAudioFromEarpiece];
189
+ } else if ([audioRoute isEqual: @"EARPIECE"] || [audioRoute isEqualToString:@"earpiece"]) {
190
+ _forceSpeakerOn = 0;
191
+ success = [self routeAudioFromEarpiece];
192
+ } else if ([audioRoute isEqualToString:@"BLUETOOTH"] || [audioRoute isEqualToString:@"bluetooth"]) {
193
+ _forceSpeakerOn = 0;
194
+ success = [self routeAudioFromBluetooth];
195
+ }
196
+ //TODO: Better Error Handling
197
+ if (success) {
198
+ resolve(@"🎉 Audio Route sucessfully changed");
199
+ } else {
200
+ reject(@"😭 error_code", @"getAudioUriJS() failed in chooseAudioRoute", RCTErrorWithMessage(@"Failed to change audio route"));
201
+ }
202
+ }
203
+ }
204
+
205
+ - (void)updateAudioDeviceState:(NSString *)audioDevice {
206
+ // check if route exist and value isn't null
207
+ return;
208
+ }
209
+
210
+ RCT_EXPORT_METHOD(stop:(NSString *)busytoneUriType)
211
+ {
212
+ if (!_audioSessionInitialized) {
213
+ return;
214
+ }
215
+
216
+ [self stopRingback];
217
+
218
+ if (busytoneUriType.length > 0 && [self startBusytone:busytoneUriType]) {
219
+ // play busytone first, and call this func again when finish
220
+ // NSLog(@"RNInCallManager.stop(): play busytone before stop");
221
+ return;
222
+ } else {
223
+ // NSLog(@"RNInCallManager.stop(): stop InCallManager");
224
+ [self restoreOriginalAudioSetup];
225
+ [self stopBusytone];
226
+ [self stopProximitySensor];
227
+ [self audioSessionSetActive:NO
228
+ options:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
229
+ callerMemo:NSStringFromSelector(_cmd)];
230
+ [self setKeepScreenOn:NO];
231
+ [self stopAudioSessionNotification];
232
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
233
+ _forceSpeakerOn = 0;
234
+ _audioSessionInitialized = NO;
235
+ }
236
+ }
237
+
238
+ RCT_EXPORT_METHOD(turnScreenOn)
239
+ {
240
+ // NSLog(@"RNInCallManager.turnScreenOn(): ios doesn't support turnScreenOn()");
241
+ }
242
+
243
+ RCT_EXPORT_METHOD(turnScreenOff)
244
+ {
245
+ // NSLog(@"RNInCallManager.turnScreenOff(): ios doesn't support turnScreenOff()");
246
+ }
247
+
248
+ RCT_EXPORT_METHOD(setFlashOn:(BOOL)enable
249
+ brightness:(nonnull NSNumber *)brightness)
250
+ {
251
+ if ([AVCaptureDevice class]) {
252
+ AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
253
+ if (device.hasTorch && device.position == AVCaptureDevicePositionBack) {
254
+ @try {
255
+ [device lockForConfiguration:nil];
256
+
257
+ if (enable) {
258
+ [device setTorchMode:AVCaptureTorchModeOn];
259
+ } else {
260
+ [device setTorchMode:AVCaptureTorchModeOff];
261
+ }
262
+
263
+ [device unlockForConfiguration];
264
+ } @catch (NSException *e) {}
265
+ }
266
+ }
267
+ }
268
+
269
+ RCT_EXPORT_METHOD(setKeepScreenOn:(BOOL)enable)
270
+ {
271
+ // NSLog(@"RNInCallManager.setKeepScreenOn(): enable: %@", enable ? @"YES" : @"NO");
272
+ dispatch_async(dispatch_get_main_queue(), ^{
273
+ [[UIApplication sharedApplication] setIdleTimerDisabled:enable];
274
+ });
275
+ }
276
+
277
+
278
+
279
+ RCT_EXPORT_METHOD(setSpeakerphoneOn:(BOOL)enable)
280
+ {
281
+ if(!enable){
282
+ [self routeAudioFromEarpiece];
283
+ } else {
284
+ [self routeAudioFromSpeakerphone];
285
+ }
286
+ }
287
+
288
+ - (BOOL)routeAudioFromBluetooth {
289
+ NSError *error = nil;
290
+ _audioSession = [AVAudioSession sharedInstance];
291
+ for (AVAudioSessionPortDescription* input in [_audioSession availableInputs]) {
292
+ if ([[input portType] isEqualToString:AVAudioSessionPortBluetoothHFP] ||
293
+ [[input portType] isEqualToString:AVAudioSessionPortBluetoothA2DP] ||
294
+ [[input portType] isEqualToString:AVAudioSessionPortBluetoothLE]) {
295
+ // Turn off current input
296
+ @try {
297
+ [_audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:&error];
298
+ } @catch (NSException *e) {
299
+ // NSLog(@"😭 Unable to override audio port none in bluetooth %@", e.reason);
300
+ return false;
301
+ }
302
+ // set current input to bluetooth
303
+ @try {
304
+ [_audioSession setPreferredInput:input error:&error];
305
+ } @catch (NSException *e) {
306
+ // NSLog(@"😭 Unable to set audio input port to bluetooth%@", e.reason);
307
+ // Set Prefeered route to speaker
308
+ [self routeAudioFromSpeakerphone];
309
+ return false;
310
+ }
311
+ }
312
+ }
313
+ return true;
314
+ }
315
+
316
+
317
+
318
+ - (BOOL)routeAudioFromEarpiece {
319
+ BOOL success;
320
+ NSError *error = nil;
321
+ // NSLog(@"Routing audio via Earpiece");
322
+ @try {
323
+
324
+ success = [_audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
325
+ if (!success) NSLog(@"Cannot set category due to error: %@", error);
326
+ success = [_audioSession setMode:AVAudioSessionModeVoiceChat error:&error];
327
+ if (!success) NSLog(@"Cannot set mode due to error: %@", error);
328
+ [_audioSession setPreferredOutputNumberOfChannels:0 error:nil];
329
+ if (!success) NSLog(@"Port override failed due to: %@", error);
330
+ [_audioSession overrideOutputAudioPort:[AVAudioSessionPortBuiltInReceiver intValue] error:&error];
331
+ success = [_audioSession setActive:YES error:&error];
332
+ if (!success) {
333
+ NSLog(@"Audio session override failed: %@", error);
334
+ return false;
335
+ }
336
+ else {
337
+ // NSLog(@"AudioSession override is successful ");
338
+ return true;
339
+ }
340
+ } @catch (NSException *e) {
341
+ NSLog(@"Error occurred while routing audio via Earpiece: %@", e.reason);
342
+ return false;
343
+ }
344
+ }
345
+
346
+ - (BOOL)routeAudioFromSpeakerphone
347
+ {
348
+ // NSLog(@"📘 Routing audio via Loudspeaker");
349
+ BOOL success;
350
+ NSError *error = nil;
351
+ @try {
352
+ success = [_audioSession setCategory:AVAudioSessionCategoryPlayAndRecord
353
+ withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker
354
+ error:nil];
355
+ if (!success) {
356
+ NSLog(@"📕 Cannot set category due to error: %@", error);
357
+ }
358
+ success = [_audioSession setMode:AVAudioSessionModeVoiceChat error: &error];
359
+ if (!success) {
360
+ NSLog(@"📕 Cannot set mode due to error: %@", error);
361
+ }
362
+ [_audioSession overrideOutputAudioPort:[AVAudioSessionPortBuiltInSpeaker intValue] error: &error];
363
+ if (!success) {
364
+ NSLog(@"📕 Port override failed due to: %@", error);
365
+ }
366
+ success = [_audioSession setActive:YES error:&error];
367
+ if (!success) {
368
+ NSLog(@"📕 Audio session override failed: %@", error);
369
+ return false;
370
+ } else {
371
+ // NSLog(@"📗 Sucessfully routed audio from Loudspeaker");
372
+ return true;
373
+ }
374
+ } @catch (NSException *e) {
375
+ NSLog(@"📕 Error occurred while routing audio via Loudspeaker: %@", e.reason);
376
+ return false;
377
+ }
378
+ }
379
+
380
+ RCT_EXPORT_METHOD(setForceSpeakerphoneOn:(int)flag)
381
+ {
382
+ _forceSpeakerOn = flag;
383
+ // NSLog(@"RNInCallManager.setForceSpeakerphoneOn(): flag: %d", flag);
384
+ [self updateAudioRoute];
385
+ }
386
+
387
+ RCT_EXPORT_METHOD(setMicrophoneMute:(BOOL)enable)
388
+ {
389
+ // NSLog(@"RNInCallManager.setMicrophoneMute(): ios doesn't support setMicrophoneMute()");
390
+ }
391
+
392
+ RCT_EXPORT_METHOD(startRingback:(NSString *)_ringbackUriType)
393
+ {
394
+ // you may rejected by apple when publish app if you use system sound instead of bundled sound.
395
+ // NSLog(@"RNInCallManager.startRingback(): type=%@", _ringbackUriType);
396
+
397
+ @try {
398
+ if (_ringback != nil) {
399
+ if ([_ringback isPlaying]) {
400
+ // NSLog(@"RNInCallManager.startRingback(): is already playing");
401
+ return;
402
+ } else {
403
+ [self stopRingback];
404
+ }
405
+ }
406
+ // ios don't have embedded DTMF tone generator. use system dtmf sound files.
407
+ NSString *ringbackUriType = [_ringbackUriType isEqualToString:@"_DTMF_"]
408
+ ? @"_DEFAULT_"
409
+ : _ringbackUriType;
410
+ NSURL *ringbackUri = [self getRingbackUri:ringbackUriType];
411
+ if (ringbackUri == nil) {
412
+ // NSLog(@"RNInCallManager.startRingback(): no available media");
413
+ return;
414
+ }
415
+ //self.storeOriginalAudioSetup()
416
+ _ringback = [[AVAudioPlayer alloc] initWithContentsOfURL:ringbackUri error:nil];
417
+ _ringback.delegate = self;
418
+ _ringback.numberOfLoops = -1; // you need to stop it explicitly
419
+ [_ringback prepareToPlay];
420
+
421
+ //self.audioSessionSetCategory(self.incallAudioCategory, [.DefaultToSpeaker, .AllowBluetooth], #function)
422
+ [self audioSessionSetCategory:_incallAudioCategory
423
+ options:0
424
+ callerMemo:NSStringFromSelector(_cmd)];
425
+ [self audioSessionSetMode:_incallAudioMode
426
+ callerMemo:NSStringFromSelector(_cmd)];
427
+ [_ringback play];
428
+ } @catch (NSException *e) {
429
+ NSLog(@"RNInCallManager.startRingback(): caught error=%@", e.reason);
430
+ }
431
+ }
432
+
433
+ RCT_EXPORT_METHOD(stopRingback)
434
+ {
435
+ if (_ringback != nil) {
436
+ // NSLog(@"RNInCallManager.stopRingback()");
437
+ [_ringback stop];
438
+ _ringback = nil;
439
+ // --- need to reset route based on config because WebRTC seems will switch audio mode automatically when call established.
440
+ //[self updateAudioRoute];
441
+ }
442
+ }
443
+
444
+ RCT_EXPORT_METHOD(startRingtone:(NSString *)ringtoneUriType
445
+ ringtoneCategory:(NSString *)ringtoneCategory)
446
+ {
447
+ // you may rejected by apple when publish app if you use system sound instead of bundled sound.
448
+ // NSLog(@"RNInCallManager.startRingtone(): type: %@", ringtoneUriType);
449
+ @try {
450
+ if (_ringtone != nil) {
451
+ if ([_ringtone isPlaying]) {
452
+ // NSLog(@"RNInCallManager.startRingtone(): is already playing.");
453
+ return;
454
+ } else {
455
+ [self stopRingtone];
456
+ }
457
+ }
458
+ NSURL *ringtoneUri = [self getRingtoneUri:ringtoneUriType];
459
+ if (ringtoneUri == nil) {
460
+ // NSLog(@"RNInCallManager.startRingtone(): no available media");
461
+ return;
462
+ }
463
+
464
+ // --- ios has Ringer/Silent switch, so just play without check ringer volume.
465
+ [self storeOriginalAudioSetup];
466
+ _ringtone = [[AVAudioPlayer alloc] initWithContentsOfURL:ringtoneUri error:nil];
467
+ _ringtone.delegate = self;
468
+ _ringtone.numberOfLoops = -1; // you need to stop it explicitly
469
+ [_ringtone prepareToPlay];
470
+
471
+ // --- 1. if we use Playback, it can supports background playing (starting from foreground), but it would not obey Ring/Silent switch.
472
+ // --- make sure you have enabled 'audio' tag ( or 'voip' tag ) at XCode -> Capabilities -> BackgroundMode
473
+ // --- 2. if we use SoloAmbient, it would obey Ring/Silent switch in the foreground, but does not support background playing,
474
+ // --- thus, then you should play ringtone again via local notification after back to home during a ring session.
475
+
476
+ // we prefer 2. by default, since most of users doesn't want to interrupted by a ringtone if Silent mode is on.
477
+
478
+ //self.audioSessionSetCategory(AVAudioSessionCategoryPlayback, [.DuckOthers], #function)
479
+ if ([ringtoneCategory isEqualToString:@"playback"]) {
480
+ [self audioSessionSetCategory:AVAudioSessionCategoryPlayback
481
+ options:0
482
+ callerMemo:NSStringFromSelector(_cmd)];
483
+ } else {
484
+ [self audioSessionSetCategory:AVAudioSessionCategorySoloAmbient
485
+ options:0
486
+ callerMemo:NSStringFromSelector(_cmd)];
487
+ }
488
+ [self audioSessionSetMode:AVAudioSessionModeDefault
489
+ callerMemo:NSStringFromSelector(_cmd)];
490
+ //[self audioSessionSetActive:YES
491
+ // options:nil
492
+ // callerMemo:NSStringFromSelector(_cmd)];
493
+ [_ringtone play];
494
+ } @catch (NSException *e) {
495
+ NSLog(@"RNInCallManager.startRingtone(): caught error = %@", e.reason);
496
+ }
497
+ }
498
+
499
+ RCT_EXPORT_METHOD(stopRingtone)
500
+ {
501
+ if (_ringtone != nil) {
502
+ // NSLog(@"RNInCallManager.stopRingtone()");
503
+ [_ringtone stop];
504
+ _ringtone = nil;
505
+ [self restoreOriginalAudioSetup];
506
+ [self audioSessionSetActive:NO
507
+ options:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
508
+ callerMemo:NSStringFromSelector(_cmd)];
509
+ }
510
+ }
511
+
512
+
513
+
514
+
515
+
516
+
517
+ RCT_EXPORT_METHOD(getAudioUriJS:(NSString *)audioType
518
+ fileType:(NSString *)fileType
519
+ resolve:(RCTPromiseResolveBlock)resolve
520
+ reject:(RCTPromiseRejectBlock)reject)
521
+ {
522
+ NSURL *result = nil;
523
+ if ([audioType isEqualToString:@"ringback"]) {
524
+ result = [self getRingbackUri:fileType];
525
+ } else if ([audioType isEqualToString:@"busytone"]) {
526
+ result = [self getBusytoneUri:fileType];
527
+ } else if ([audioType isEqualToString:@"ringtone"]) {
528
+ result = [self getRingtoneUri:fileType];
529
+ }
530
+ if (result != nil) {
531
+ if (result.absoluteString.length > 0) {
532
+ resolve(result.absoluteString);
533
+ return;
534
+ }
535
+ }
536
+ reject(@"error_code", @"getAudioUriJS() failed in main fn", RCTErrorWithMessage(@"getAudioUriJS() failed"));
537
+ }
538
+
539
+ RCT_EXPORT_METHOD(getIsWiredHeadsetPluggedIn:(RCTPromiseResolveBlock)resolve
540
+ reject:(RCTPromiseRejectBlock)reject)
541
+ {
542
+ BOOL wiredHeadsetPluggedIn = [self isWiredHeadsetPluggedIn];
543
+ resolve(wiredHeadsetPluggedIn ? @YES : @NO);
544
+ }
545
+
546
+
547
+ RCT_EXPORT_METHOD(isBluetoothHeadsetConnected: (RCTPromiseResolveBlock) resolve
548
+ reject:(RCTPromiseRejectBlock) reject)
549
+ {
550
+ // NSLog(@"👉 Checking if Bluetooth device is connected");
551
+ NSMutableArray *devices = [NSMutableArray array];
552
+ AVAudioSessionRouteDescription* route = [[AVAudioSession sharedInstance] currentRoute];
553
+ for (AVAudioSessionPortDescription* desc in [route outputs]) {
554
+ // NSLog(@"🧑‍💻 Bluetooth Desc: %@", desc);
555
+ if ([[desc portType] isEqualToString:AVAudioSessionPortBluetoothHFP] ||
556
+ [[desc portType] isEqualToString:AVAudioSessionPortBluetoothA2DP] ||
557
+ [[desc portType] isEqualToString:AVAudioSessionPortBluetoothLE]) {
558
+ [devices addObject:[desc portName]];
559
+ }
560
+ }
561
+ // NSLog(@"🧑‍💻 Bluetooth Devices %@", devices);
562
+ if (!devices || !devices.count){
563
+ resolve(@NO);
564
+ } else {
565
+ resolve(@YES);
566
+ }
567
+ }
568
+
569
+ //TODO: Rewrite this function so that we manage bluetooth, wiredHeadset and all the updates from here
570
+ - (BOOL)updateAudioRoute
571
+ {
572
+ // NSLog(@" 👉 RNInCallManager.updateAudioRoute(): [Enter] forceSpeakerOn flag=%d media=%@ category=%@ mode=%@", _forceSpeakerOn, _media, _audioSession.category, _audioSession.mode);
573
+ //self.debugAudioSession()
574
+
575
+ //AVAudioSessionPortOverride overrideAudioPort;
576
+ int overrideAudioPort;
577
+ NSString *overrideAudioPortString = @"";
578
+ NSString *audioMode = @"";
579
+
580
+ // --- WebRTC native code will change audio mode automatically when established.
581
+ // --- It would have some race condition if we change audio mode with webrtc at the same time.
582
+ // --- So we should not change audio mode as possible as we can. Only when default video call which wants to force speaker off.
583
+ // --- audio: only override speaker on/off; video: should change category if needed and handle proximity sensor. ( because default proximity is off when video call )
584
+ if (_forceSpeakerOn == 1) {
585
+ // NSLog(@"👨‍💻 Force Speaker on");
586
+ // --- force ON, override speaker only, keep audio mode remain.
587
+ overrideAudioPort = AVAudioSessionPortOverrideSpeaker;
588
+ overrideAudioPortString = @".Speaker";
589
+ // if ([_media isEqualToString:@"video"]) {
590
+ // audioMode = AVAudioSessionModeVideoChat;
591
+ // [self stopProximitySensor];
592
+ // }
593
+ //Bluetooth device
594
+ } else { // use default behavior
595
+ // NSLog(@"👨‍💻 Else Audio Route");
596
+ overrideAudioPort = AVAudioSessionPortOverrideNone;
597
+ overrideAudioPortString = @".None";
598
+ // if ([_media isEqualToString:@"video"]) {
599
+ // audioMode = AVAudioSessionModeVideoChat;
600
+ // [self stopProximitySensor];
601
+ // }
602
+ }
603
+ //NOTE: Check Current route to speaker
604
+ BOOL isCurrentRouteToSpeaker;
605
+ isCurrentRouteToSpeaker = [self checkAudioRoute:@[AVAudioSessionPortBuiltInSpeaker]
606
+ routeType:@"output"];
607
+ if ((overrideAudioPort == AVAudioSessionPortOverrideSpeaker && !isCurrentRouteToSpeaker)
608
+ || (overrideAudioPort == AVAudioSessionPortOverrideNone && isCurrentRouteToSpeaker)) {
609
+ @try {
610
+ [_audioSession overrideOutputAudioPort:overrideAudioPort error:nil];
611
+ // NSLog(@"💯 RNInCallManager.updateAudioRoute(): audioSession.overrideOutputAudioPort(%@) success", overrideAudioPortString);
612
+ } @catch (NSException *e) {
613
+ NSLog(@"😭 RNInCallManager.updateAudioRoute(): audioSession.overrideOutputAudioPort(%@) fail: %@", overrideAudioPortString, e.reason);
614
+ }
615
+ } else {
616
+ // NSLog(@"👨‍💻 RNInCallManager.updateAudioRoute(): did NOT overrideOutputAudioPort()");
617
+ }
618
+
619
+ if (audioMode.length > 0 && ![_audioSession.mode isEqualToString:audioMode]) {
620
+ [self audioSessionSetMode:audioMode
621
+ callerMemo:NSStringFromSelector(_cmd)];
622
+
623
+ [self sendEventWithName:@"onAudioDeviceChanged" body:@{@"devices": audioMode}];
624
+ // NSLog(@"👨‍💻 RNInCallManager.updateAudioRoute() audio mode has changed to %@", audioMode);
625
+ return true;
626
+ } else {
627
+ // NSLog(@"👨‍💻 RNInCallManager.updateAudioRoute() did NOT change audio mode");
628
+ return false;
629
+ }
630
+ //self.debugAudioSession()
631
+ }
632
+
633
+ - (BOOL)checkAudioRoute:(NSArray<NSString *> *)targetPortTypeArray
634
+ routeType:(NSString *)routeType
635
+ {
636
+ //TODO: Checking bluetooth route should be called from here
637
+ AVAudioSessionRouteDescription *currentRoute = _audioSession.currentRoute;
638
+ // NSLog(@"👉 Check Audio Route called ");
639
+ // NSLog(@"👨‍💻 Route Type: %@", routeType);
640
+ // NSLog(@"👨‍💻 Current Audio Route: %@", currentRoute);
641
+ // NSLog(@"👨‍💻 targetPortTypeArray: %@", targetPortTypeArray);
642
+ if (currentRoute != nil) {
643
+ NSArray<AVAudioSessionPortDescription *> *routes = [routeType isEqualToString:@"input"]
644
+ ? currentRoute.inputs
645
+ : currentRoute.outputs;
646
+ for (AVAudioSessionPortDescription *portDescription in routes) {
647
+ if ([targetPortTypeArray containsObject:portDescription.portType]) {
648
+ // NSLog(@"🌏 Audio Route for %@ does exist", targetPortTypeArray);
649
+ return YES;
650
+ }
651
+ }
652
+ }
653
+ // NSLog(@"🌏 Audio Route for %@ does not exist", targetPortTypeArray);
654
+ return NO;
655
+ }
656
+
657
+ - (BOOL)startBusytone:(NSString *)_busytoneUriType
658
+ {
659
+ // you may rejected by apple when publish app if you use system sound instead of bundled sound.
660
+ NSLog(@"RNInCallManager.startBusytone(): type: %@", _busytoneUriType);
661
+ @try {
662
+ if (_busytone != nil) {
663
+ if ([_busytone isPlaying]) {
664
+ // NSLog(@"RNInCallManager.startBusytone(): is already playing");
665
+ return NO;
666
+ } else {
667
+ [self stopBusytone];
668
+ }
669
+ }
670
+
671
+ // ios don't have embedded DTMF tone generator. use system dtmf sound files.
672
+ NSString *busytoneUriType = [_busytoneUriType isEqualToString:@"_DTMF_"]
673
+ ? @"_DEFAULT_"
674
+ : _busytoneUriType;
675
+ NSURL *busytoneUri = [self getBusytoneUri:busytoneUriType];
676
+ if (busytoneUri == nil) {
677
+ // NSLog(@"RNInCallManager.startBusytone(): no available media");
678
+ return NO;
679
+ }
680
+ //[self storeOriginalAudioSetup];
681
+ _busytone = [[AVAudioPlayer alloc] initWithContentsOfURL:busytoneUri error:nil];
682
+ _busytone.delegate = self;
683
+ _busytone.numberOfLoops = 0; // it's part of start(), will stop at stop()
684
+ [_busytone prepareToPlay];
685
+
686
+ //self.audioSessionSetCategory(self.incallAudioCategory, [.DefaultToSpeaker, .AllowBluetooth], #function)
687
+ [self audioSessionSetCategory:_incallAudioCategory
688
+ options:0
689
+ callerMemo:NSStringFromSelector(_cmd)];
690
+ [self audioSessionSetMode:_incallAudioMode
691
+ callerMemo:NSStringFromSelector(_cmd)];
692
+ [_busytone play];
693
+ } @catch (NSException *e) {
694
+ // NSLog(@"RNInCallManager.startBusytone(): caught error = %@", e.reason);
695
+ return NO;
696
+ }
697
+ return YES;
698
+ }
699
+
700
+ - (void)stopBusytone
701
+ {
702
+ if (_busytone != nil) {
703
+ // NSLog(@"RNInCallManager.stopBusytone()");
704
+ [_busytone stop];
705
+ _busytone = nil;
706
+ }
707
+ }
708
+
709
+ - (BOOL)isWiredHeadsetPluggedIn
710
+ {
711
+ // NSLog(@"👉 checking if isWiredHeadset is plusgged in or not");
712
+ // --- only check for a audio device plugged into headset port instead bluetooth/usb/hdmi
713
+ return [self checkAudioRoute:@[AVAudioSessionPortHeadphones]
714
+ routeType:@"output"]
715
+ || [self checkAudioRoute:@[AVAudioSessionPortHeadsetMic]
716
+ routeType:@"input"];
717
+ }
718
+
719
+ - (void)audioSessionSetCategory:(NSString *)audioCategory
720
+ options:(AVAudioSessionCategoryOptions)options
721
+ callerMemo:(NSString *)callerMemo
722
+ {
723
+ @try {
724
+ if (options != 0) {
725
+ [_audioSession setCategory:audioCategory
726
+ withOptions:options
727
+ error:nil];
728
+ } else {
729
+ [_audioSession setCategory:audioCategory
730
+ error:nil];
731
+ }
732
+ // NSLog(@"RNInCallManager.%@: audioSession.setCategory: %@, withOptions: %lu success", callerMemo, audioCategory, (unsigned long)options);
733
+ } @catch (NSException *e) {
734
+ // NSLog(@"RNInCallManager.%@: audioSession.setCategory: %@, withOptions: %lu fail: %@", callerMemo, audioCategory, (unsigned long)options, e.reason);
735
+ }
736
+ }
737
+
738
+ - (void)audioSessionSetMode:(NSString *)audioMode
739
+ callerMemo:(NSString *)callerMemo
740
+ {
741
+ @try {
742
+ [_audioSession setMode:audioMode error:nil];
743
+ // NSLog(@"RNInCallManager.%@: audioSession.setMode(%@) success", callerMemo, audioMode);
744
+ } @catch (NSException *e) {
745
+ // NSLog(@"RNInCallManager.%@: audioSession.setMode(%@) fail: %@", callerMemo, audioMode, e.reason);
746
+ }
747
+ }
748
+
749
+ - (void)audioSessionSetActive:(BOOL)audioActive
750
+ options:(AVAudioSessionSetActiveOptions)options
751
+ callerMemo:(NSString *)callerMemo
752
+ {
753
+ @try {
754
+ if (options != 0) {
755
+ [_audioSession setActive:audioActive
756
+ withOptions:options
757
+ error:nil];
758
+ } else {
759
+ [_audioSession setActive:audioActive
760
+ error:nil];
761
+ }
762
+ // NSLog(@"RNInCallManager.%@: audioSession.setActive(%@), withOptions: %lu success", callerMemo, audioActive ? @"YES" : @"NO", (unsigned long)options);
763
+ } @catch (NSException *e) {
764
+ // NSLog(@"RNInCallManager.%@: audioSession.setActive(%@), withOptions: %lu fail: %@", callerMemo, audioActive ? @"YES" : @"NO", (unsigned long)options, e.reason);
765
+ }
766
+ }
767
+
768
+ - (void)storeOriginalAudioSetup
769
+ {
770
+ // NSLog(@"👨‍💻 RNInCallManager.storeOriginalAudioSetup(): origAudioCategory=%@, origAudioMode=%@", _audioSession.category, _audioSession.mode);
771
+ _origAudioCategory = _audioSession.category;
772
+ _origAudioMode = _audioSession.mode;
773
+ }
774
+
775
+ - (void)restoreOriginalAudioSetup
776
+ {
777
+ // NSLog(@"RNInCallManager.restoreOriginalAudioSetup(): origAudioCategory=%@, origAudioMode=%@", _audioSession.category, _audioSession.mode);
778
+ [self audioSessionSetCategory:_origAudioCategory
779
+ options:0
780
+ callerMemo:NSStringFromSelector(_cmd)];
781
+ [self audioSessionSetMode:_origAudioMode
782
+ callerMemo:NSStringFromSelector(_cmd)];
783
+ }
784
+
785
+ - (void)startProximitySensor
786
+ {
787
+ if (_isProximityRegistered) {
788
+ return;
789
+ }
790
+
791
+ // NSLog(@"RNInCallManager.startProximitySensor()");
792
+ dispatch_async(dispatch_get_main_queue(), ^{
793
+ self->_currentDevice.proximityMonitoringEnabled = YES;
794
+ });
795
+
796
+ // --- in case it didn't deallocate when ViewDidUnload
797
+ [self stopObserve:_proximityObserver
798
+ name:UIDeviceProximityStateDidChangeNotification
799
+ object:nil];
800
+
801
+ _proximityObserver = [self startObserve:UIDeviceProximityStateDidChangeNotification
802
+ object:_currentDevice
803
+ queue: nil
804
+ block:^(NSNotification *notification) {
805
+ BOOL state = self->_currentDevice.proximityState;
806
+ if (state != self->_proximityIsNear) {
807
+ // NSLog(@"RNInCallManager.UIDeviceProximityStateDidChangeNotification(): isNear: %@", state ? @"YES" : @"NO");
808
+ self->_proximityIsNear = state;
809
+ [self sendEventWithName:@"Proximity" body:@{@"isNear": state ? @YES : @NO}];
810
+ }
811
+ }];
812
+
813
+ _isProximityRegistered = YES;
814
+ }
815
+
816
+ - (void)stopProximitySensor
817
+ {
818
+ if (!_isProximityRegistered) {
819
+ return;
820
+ }
821
+
822
+ // NSLog(@"RNInCallManager.stopProximitySensor()");
823
+ dispatch_async(dispatch_get_main_queue(), ^{
824
+ self->_currentDevice.proximityMonitoringEnabled = NO;
825
+ });
826
+
827
+ // --- remove all no matter what object
828
+ [self stopObserve:_proximityObserver
829
+ name:UIDeviceProximityStateDidChangeNotification
830
+ object:nil];
831
+
832
+ _isProximityRegistered = NO;
833
+ }
834
+
835
+ - (void)startAudioSessionNotification
836
+ {
837
+ // NSLog(@"👉 RNInCallManager.startAudioSessionNotification() starting...");
838
+ [self startAudioSessionInterruptionNotification];
839
+ [self startAudioSessionRouteChangeNotification];
840
+ [self startAudioSessionMediaServicesWereLostNotification];
841
+ [self startAudioSessionMediaServicesWereResetNotification];
842
+ [self startAudioSessionSilenceSecondaryAudioHintNotification];
843
+ }
844
+
845
+ - (void)stopAudioSessionNotification
846
+ {
847
+ // NSLog(@"👉 RNInCallManager.startAudioSessionNotification() stopping...");
848
+ [self stopAudioSessionInterruptionNotification];
849
+ [self stopAudioSessionRouteChangeNotification];
850
+ [self stopAudioSessionMediaServicesWereLostNotification];
851
+ [self stopAudioSessionMediaServicesWereResetNotification];
852
+ [self stopAudioSessionSilenceSecondaryAudioHintNotification];
853
+ }
854
+
855
+ - (void)startAudioSessionInterruptionNotification
856
+ {
857
+ if (_isAudioSessionInterruptionRegistered) {
858
+ return;
859
+ }
860
+ // NSLog(@"👉 RNInCallManager.startAudioSessionInterruptionNotification()");
861
+
862
+ // --- in case it didn't deallocate when ViewDidUnload
863
+ [self stopObserve:_audioSessionInterruptionObserver
864
+ name:AVAudioSessionInterruptionNotification
865
+ object:nil];
866
+
867
+ _audioSessionInterruptionObserver = [self startObserve:AVAudioSessionInterruptionNotification
868
+ object:nil
869
+ queue:nil
870
+ block:^(NSNotification *notification) {
871
+ if (notification.userInfo == nil
872
+ || ![notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
873
+ return;
874
+ }
875
+
876
+ //NSUInteger rawValue = notification.userInfo[AVAudioSessionInterruptionTypeKey].unsignedIntegerValue;
877
+ NSNumber *interruptType = [notification.userInfo objectForKey:@"AVAudioSessionInterruptionTypeKey"];
878
+ if ([interruptType unsignedIntegerValue] == AVAudioSessionInterruptionTypeBegan) {
879
+ // NSLog(@"RNInCallManager.AudioSessionInterruptionNotification: Began");
880
+ } else if ([interruptType unsignedIntegerValue] == AVAudioSessionInterruptionTypeEnded) {
881
+ // NSLog(@"🧑‍💻 RNInCallManager.AudioSessionInterruptionNotification: Ended");
882
+ } else {
883
+ // NSLog(@"🧑‍💻 RNInCallManager.AudioSessionInterruptionNotification: Unknow Value");
884
+ }
885
+ //NSLog(@"RNInCallManager.AudioSessionInterruptionNotification: could not resolve notification");
886
+ }];
887
+
888
+ _isAudioSessionInterruptionRegistered = YES;
889
+ }
890
+
891
+ - (void)stopAudioSessionInterruptionNotification
892
+ {
893
+ if (!_isAudioSessionInterruptionRegistered) {
894
+ return;
895
+ }
896
+ // NSLog(@"RNInCallManager.stopAudioSessionInterruptionNotification()");
897
+ // --- remove all no matter what object
898
+ [self stopObserve:_audioSessionInterruptionObserver
899
+ name:AVAudioSessionInterruptionNotification
900
+ object: nil];
901
+ _isAudioSessionInterruptionRegistered = NO;
902
+ }
903
+
904
+ - (void)startAudioSessionRouteChangeNotification
905
+ {
906
+ if (_isAudioSessionRouteChangeRegistered) {
907
+ return;
908
+ }
909
+
910
+ // NSLog(@"RNInCallManager.startAudioSessionRouteChangeNotification()");
911
+
912
+ // --- in case it didn't deallocate when ViewDidUnload
913
+ [self stopObserve:_audioSessionRouteChangeObserver
914
+ name: AVAudioSessionRouteChangeNotification
915
+ object: nil];
916
+ //NOTE: On Audio Change this function is called
917
+ _audioSessionRouteChangeObserver = [self startObserve:AVAudioSessionRouteChangeNotification
918
+ object: nil
919
+ queue: nil
920
+ block:^(NSNotification *notification) {
921
+ // NSLog(@"🧑‍💻 Audio Session changed, user route %@ and %@ current route", _userSelectedAudioRoute, self->_audioSession.currentRoute);
922
+ if (notification.userInfo == nil
923
+ || ![notification.name isEqualToString:AVAudioSessionRouteChangeNotification]) {
924
+ return;
925
+ }
926
+
927
+ NSNumber *routeChangeType = [notification.userInfo objectForKey:@"AVAudioSessionRouteChangeReasonKey"];
928
+ NSUInteger routeChangeTypeValue = [routeChangeType unsignedIntegerValue];
929
+
930
+ switch (routeChangeTypeValue) {
931
+ case AVAudioSessionRouteChangeReasonUnknown:
932
+ // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: Unknown");
933
+ break;
934
+ case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
935
+ // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: NewDeviceAvailable");
936
+ if ([self checkAudioRoute:@[AVAudioSessionPortHeadsetMic]
937
+ routeType:@"input"]) {
938
+ _forceSpeakerOn = 0;
939
+ [self sendEventWithName:@"onAudioDeviceChanged"
940
+ body:@{
941
+ @"availableAudioDeviceList": @"[speaker, earpiece, wired]",
942
+ @"selectedAudioDevice": AVAudioSessionPortHeadsetMic,
943
+ }];
944
+ } else if ([self checkAudioRoute:@[AVAudioSessionPortHeadphones]
945
+ routeType:@"output"]) {
946
+ _forceSpeakerOn = 0;
947
+ [self sendEventWithName:@"onAudioDeviceChanged"
948
+ body:@{
949
+ @"availableAudioDeviceList": @"[speaker, earpiece, wired]",
950
+ @"selectedAudioDevice": AVAudioSessionPortHeadphones,
951
+ }];
952
+ } else if ([self checkAudioRoute:@[AVAudioSessionPortBluetoothHFP]
953
+ routeType:@"input"]) {
954
+ _forceSpeakerOn = 0;
955
+ [self sendEventWithName:@"onAudioDeviceChanged"
956
+ body:@{
957
+ @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
958
+ @"selectedAudioDevice": AVAudioSessionPortBluetoothHFP,
959
+ }];
960
+ } else if ([self checkAudioRoute:@[AVAudioSessionPortBluetoothA2DP]
961
+ routeType:@"input"]) {
962
+ _forceSpeakerOn = 0;
963
+ [self sendEventWithName:@"onAudioDeviceChanged"
964
+ body:@{
965
+ @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
966
+ @"selectedAudioDevice": AVAudioSessionPortBluetoothA2DP,
967
+ }];
968
+ } else if ([self checkAudioRoute:@[AVAudioSessionPortBluetoothLE]
969
+ routeType:@"input"]) {
970
+ _forceSpeakerOn = 0;
971
+ [self sendEventWithName:@"onAudioDeviceChanged"
972
+ body:@{
973
+ @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
974
+ @"selectedAudioDevice": AVAudioSessionPortBluetoothLE,
975
+ }];
976
+ }
977
+ break;
978
+ case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
979
+ [self sendEventWithName:@"onAudioDeviceChanged"
980
+ body:@{
981
+ @"availableAudioDeviceList": @"[speaker, earpiece]",
982
+ @"selectedAudioDevice": @"speaker",
983
+ }];
984
+ break;
985
+ case AVAudioSessionRouteChangeReasonCategoryChange:
986
+ // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: CategoryChange. category=%@ mode=%@", self->_audioSession.category, self->_audioSession.mode);
987
+ break;
988
+ case AVAudioSessionRouteChangeReasonOverride:
989
+ // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: Override");
990
+ [self sendEventWithName:@"onAudioDeviceChanged"
991
+ body:@{
992
+ @"availableAudioDeviceList": @"[speaker, earpiece]",
993
+ @"selectedAudioDevice": @"speaker",
994
+ }];
995
+ break;
996
+ case AVAudioSessionRouteChangeReasonWakeFromSleep:
997
+ // NSLog(@"RNInCallManager.AudioRouteChange.Reason: WakeFromSleep");
998
+ break;
999
+ case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
1000
+ // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: NoSuitableRouteForCategory");
1001
+ break;
1002
+ case AVAudioSessionRouteChangeReasonRouteConfigurationChange:
1003
+ // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: RouteConfigurationChange. category=%@ mode=%@", self->_audioSession.category, self->_audioSession.mode);
1004
+ break;
1005
+ default:
1006
+ // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: Unknow Value");
1007
+ break;
1008
+ }
1009
+ [self updateAudioRoute];
1010
+
1011
+ NSNumber *silenceSecondaryAudioHintType = [notification.userInfo objectForKey:@"AVAudioSessionSilenceSecondaryAudioHintTypeKey"];
1012
+ NSUInteger silenceSecondaryAudioHintTypeValue = [silenceSecondaryAudioHintType unsignedIntegerValue];
1013
+ // switch (silenceSecondaryAudioHintTypeValue) {
1014
+ // case AVAudioSessionSilenceSecondaryAudioHintTypeBegin:
1015
+ // // NSLog(@"RNInCallManager.AudioRouteChange.SilenceSecondaryAudioHint: Begin");
1016
+ // case AVAudioSessionSilenceSecondaryAudioHintTypeEnd:
1017
+ // // NSLog(@"RNInCallManager.AudioRouteChange.SilenceSecondaryAudioHint: End");
1018
+ // default:
1019
+ // // NSLog(@"RNInCallManager.AudioRouteChange.SilenceSecondaryAudioHint: Unknow Value");
1020
+ // }
1021
+ }];
1022
+
1023
+ _isAudioSessionRouteChangeRegistered = YES;
1024
+ }
1025
+
1026
+ - (void)stopAudioSessionRouteChangeNotification
1027
+ {
1028
+ if (!_isAudioSessionRouteChangeRegistered) {
1029
+ return;
1030
+ }
1031
+
1032
+ // NSLog(@"RNInCallManager.stopAudioSessionRouteChangeNotification()");
1033
+ // --- remove all no matter what object
1034
+ [self stopObserve:_audioSessionRouteChangeObserver
1035
+ name:AVAudioSessionRouteChangeNotification
1036
+ object:nil];
1037
+ _isAudioSessionRouteChangeRegistered = NO;
1038
+ }
1039
+
1040
+ - (void)startAudioSessionMediaServicesWereLostNotification
1041
+ {
1042
+ if (_isAudioSessionMediaServicesWereLostRegistered) {
1043
+ return;
1044
+ }
1045
+
1046
+ // NSLog(@"RNInCallManager.startAudioSessionMediaServicesWereLostNotification()");
1047
+
1048
+ // --- in case it didn't deallocate when ViewDidUnload
1049
+ [self stopObserve:_audioSessionMediaServicesWereLostObserver
1050
+ name:AVAudioSessionMediaServicesWereLostNotification
1051
+ object:nil];
1052
+
1053
+ _audioSessionMediaServicesWereLostObserver = [self startObserve:AVAudioSessionMediaServicesWereLostNotification
1054
+ object:nil
1055
+ queue:nil
1056
+ block:^(NSNotification *notification) {
1057
+ // --- This notification has no userInfo dictionary.
1058
+ // NSLog(@"RNInCallManager.AudioSessionMediaServicesWereLostNotification: Media Services Were Lost");
1059
+ }];
1060
+
1061
+ _isAudioSessionMediaServicesWereLostRegistered = YES;
1062
+ }
1063
+
1064
+ - (void)stopAudioSessionMediaServicesWereLostNotification
1065
+ {
1066
+ if (!_isAudioSessionMediaServicesWereLostRegistered) {
1067
+ return;
1068
+ }
1069
+
1070
+ // NSLog(@"RNInCallManager.stopAudioSessionMediaServicesWereLostNotification()");
1071
+
1072
+ // --- remove all no matter what object
1073
+ [self stopObserve:_audioSessionMediaServicesWereLostObserver
1074
+ name:AVAudioSessionMediaServicesWereLostNotification
1075
+ object:nil];
1076
+
1077
+ _isAudioSessionMediaServicesWereLostRegistered = NO;
1078
+ }
1079
+
1080
+ - (void)startAudioSessionMediaServicesWereResetNotification
1081
+ {
1082
+ if (_isAudioSessionMediaServicesWereResetRegistered) {
1083
+ return;
1084
+ }
1085
+
1086
+ // NSLog(@"RNInCallManager.startAudioSessionMediaServicesWereResetNotification()");
1087
+
1088
+ // --- in case it didn't deallocate when ViewDidUnload
1089
+ [self stopObserve:_audioSessionMediaServicesWereResetObserver
1090
+ name:AVAudioSessionMediaServicesWereResetNotification
1091
+ object:nil];
1092
+
1093
+ _audioSessionMediaServicesWereResetObserver = [self startObserve:AVAudioSessionMediaServicesWereResetNotification
1094
+ object:nil
1095
+ queue:nil
1096
+ block:^(NSNotification *notification) {
1097
+ // --- This notification has no userInfo dictionary.
1098
+ // NSLog(@"RNInCallManager.AudioSessionMediaServicesWereResetNotification: Media Services Were Reset");
1099
+ }];
1100
+
1101
+ _isAudioSessionMediaServicesWereResetRegistered = YES;
1102
+ }
1103
+
1104
+ - (void)stopAudioSessionMediaServicesWereResetNotification
1105
+ {
1106
+ if (!_isAudioSessionMediaServicesWereResetRegistered) {
1107
+ return;
1108
+ }
1109
+
1110
+ // NSLog(@"RNInCallManager.stopAudioSessionMediaServicesWereResetNotification()");
1111
+
1112
+ // --- remove all no matter what object
1113
+ [self stopObserve:_audioSessionMediaServicesWereResetObserver
1114
+ name:AVAudioSessionMediaServicesWereResetNotification
1115
+ object:nil];
1116
+
1117
+ _isAudioSessionMediaServicesWereResetRegistered = NO;
1118
+ }
1119
+
1120
+ - (void)startAudioSessionSilenceSecondaryAudioHintNotification
1121
+ {
1122
+ if (_isAudioSessionSilenceSecondaryAudioHintRegistered) {
1123
+ return;
1124
+ }
1125
+
1126
+ // NSLog(@"RNInCallManager.startAudioSessionSilenceSecondaryAudioHintNotification()");
1127
+
1128
+ // --- in case it didn't deallocate when ViewDidUnload
1129
+ [self stopObserve:_audioSessionSilenceSecondaryAudioHintObserver
1130
+ name:AVAudioSessionSilenceSecondaryAudioHintNotification
1131
+ object:nil];
1132
+
1133
+ _audioSessionSilenceSecondaryAudioHintObserver = [self startObserve:AVAudioSessionSilenceSecondaryAudioHintNotification
1134
+ object:nil
1135
+ queue:nil
1136
+ block:^(NSNotification *notification) {
1137
+ if (notification.userInfo == nil
1138
+ || ![notification.name isEqualToString:AVAudioSessionSilenceSecondaryAudioHintNotification]) {
1139
+ return;
1140
+ }
1141
+
1142
+ NSNumber *silenceSecondaryAudioHintType = [notification.userInfo objectForKey:@"AVAudioSessionSilenceSecondaryAudioHintTypeKey"];
1143
+ NSUInteger silenceSecondaryAudioHintTypeValue = [silenceSecondaryAudioHintType unsignedIntegerValue];
1144
+ switch (silenceSecondaryAudioHintTypeValue) {
1145
+ case AVAudioSessionSilenceSecondaryAudioHintTypeBegin:
1146
+ // NSLog(@"RNInCallManager.AVAudioSessionSilenceSecondaryAudioHintNotification: Begin");
1147
+ break;
1148
+ case AVAudioSessionSilenceSecondaryAudioHintTypeEnd:
1149
+ // NSLog(@"RNInCallManager.AVAudioSessionSilenceSecondaryAudioHintNotification: End");
1150
+ break;
1151
+ default:
1152
+ // NSLog(@"RNInCallManager.AVAudioSessionSilenceSecondaryAudioHintNotification: Unknow Value");
1153
+ break;
1154
+ }
1155
+ }];
1156
+ _isAudioSessionSilenceSecondaryAudioHintRegistered = YES;
1157
+ }
1158
+
1159
+ - (void)stopAudioSessionSilenceSecondaryAudioHintNotification
1160
+ {
1161
+ if (!_isAudioSessionSilenceSecondaryAudioHintRegistered) {
1162
+ return;
1163
+ }
1164
+
1165
+ // NSLog(@"RNInCallManager.stopAudioSessionSilenceSecondaryAudioHintNotification()");
1166
+ // --- remove all no matter what object
1167
+ [self stopObserve:_audioSessionSilenceSecondaryAudioHintObserver
1168
+ name:AVAudioSessionSilenceSecondaryAudioHintNotification
1169
+ object:nil];
1170
+
1171
+ _isAudioSessionSilenceSecondaryAudioHintRegistered = NO;
1172
+ }
1173
+
1174
+ - (id)startObserve:(NSString *)name
1175
+ object:(id)object
1176
+ queue:(NSOperationQueue *)queue
1177
+ block:(void (^)(NSNotification *))block
1178
+ {
1179
+ return [[NSNotificationCenter defaultCenter] addObserverForName:name
1180
+ object:object
1181
+ queue:queue
1182
+ usingBlock:block];
1183
+ }
1184
+
1185
+ - (void)stopObserve:(id)observer
1186
+ name:(NSString *)name
1187
+ object:(id)object
1188
+ {
1189
+ if (observer == nil) return;
1190
+ [[NSNotificationCenter defaultCenter] removeObserver:observer
1191
+ name:name
1192
+ object:object];
1193
+ }
1194
+
1195
+ - (NSURL *)getRingbackUri:(NSString *)_type
1196
+ {
1197
+ NSString *fileBundle = @"incallmanager_ringback";
1198
+ NSString *fileBundleExt = @"mp3";
1199
+ //NSString *fileSysWithExt = @"vc~ringing.caf"; // --- ringtone of facetime, but can't play it.
1200
+ //NSString *fileSysPath = @"/System/Library/Audio/UISounds";
1201
+ NSString *fileSysWithExt = @"Marimba.m4r";
1202
+ NSString *fileSysPath = @"/Library/Ringtones";
1203
+
1204
+ // --- you can't get default user perfrence sound in ios
1205
+ NSString *type = [_type isEqualToString:@""] || [_type isEqualToString:@"_DEFAULT_"]
1206
+ ? fileSysWithExt
1207
+ : _type;
1208
+
1209
+ NSURL *bundleUri = _bundleRingbackUri;
1210
+ NSURL *defaultUri = _defaultRingbackUri;
1211
+
1212
+ NSURL *uri = [self getAudioUri:type
1213
+ fileBundle:fileBundle
1214
+ fileBundleExt:fileBundleExt
1215
+ fileSysWithExt:fileSysWithExt
1216
+ fileSysPath:fileSysPath
1217
+ uriBundle:&bundleUri
1218
+ uriDefault:&defaultUri];
1219
+
1220
+ _bundleRingbackUri = bundleUri;
1221
+ _defaultRingbackUri = defaultUri;
1222
+
1223
+ return uri;
1224
+ }
1225
+
1226
+ - (NSURL *)getBusytoneUri:(NSString *)_type
1227
+ {
1228
+ NSString *fileBundle = @"incallmanager_busytone";
1229
+ NSString *fileBundleExt = @"mp3";
1230
+ NSString *fileSysWithExt = @"ct-busy.caf"; //ct-congestion.caf
1231
+ NSString *fileSysPath = @"/System/Library/Audio/UISounds";
1232
+ // --- you can't get default user perfrence sound in ios
1233
+ NSString *type = [_type isEqualToString:@""] || [_type isEqualToString:@"_DEFAULT_"]
1234
+ ? fileSysWithExt
1235
+ : _type;
1236
+
1237
+ NSURL *bundleUri = _bundleBusytoneUri;
1238
+ NSURL *defaultUri = _defaultBusytoneUri;
1239
+
1240
+ NSURL *uri = [self getAudioUri:type
1241
+ fileBundle:fileBundle
1242
+ fileBundleExt:fileBundleExt
1243
+ fileSysWithExt:fileSysWithExt
1244
+ fileSysPath:fileSysPath
1245
+ uriBundle:&bundleUri
1246
+ uriDefault:&defaultUri];
1247
+
1248
+ _bundleBusytoneUri = bundleUri;
1249
+ _defaultBusytoneUri = defaultUri;
1250
+
1251
+ return uri;
1252
+ }
1253
+
1254
+ - (NSURL *)getRingtoneUri:(NSString *)_type
1255
+ {
1256
+ NSString *fileBundle = @"incallmanager_ringtone";
1257
+ NSString *fileBundleExt = @"mp3";
1258
+ NSString *fileSysWithExt = @"Opening.m4r"; //Marimba.m4r
1259
+ NSString *fileSysPath = @"/Library/Ringtones";
1260
+ // --- you can't get default user perfrence sound in ios
1261
+ NSString *type = [_type isEqualToString:@""] || [_type isEqualToString:@"_DEFAULT_"]
1262
+ ? fileSysWithExt
1263
+ : _type;
1264
+
1265
+ NSURL *bundleUri = _bundleRingtoneUri;
1266
+ NSURL *defaultUri = _defaultRingtoneUri;
1267
+
1268
+ NSURL *uri = [self getAudioUri:type
1269
+ fileBundle:fileBundle
1270
+ fileBundleExt:fileBundleExt
1271
+ fileSysWithExt:fileSysWithExt
1272
+ fileSysPath:fileSysPath
1273
+ uriBundle:&bundleUri
1274
+ uriDefault:&defaultUri];
1275
+
1276
+ _bundleRingtoneUri = bundleUri;
1277
+ _defaultRingtoneUri = defaultUri;
1278
+
1279
+ return uri;
1280
+ }
1281
+
1282
+ - (NSURL *)getAudioUri:(NSString *)_type
1283
+ fileBundle:(NSString *)fileBundle
1284
+ fileBundleExt:(NSString *)fileBundleExt
1285
+ fileSysWithExt:(NSString *)fileSysWithExt
1286
+ fileSysPath:(NSString *)fileSysPath
1287
+ uriBundle:(NSURL **)uriBundle
1288
+ uriDefault:(NSURL **)uriDefault
1289
+ {
1290
+ NSString *type = _type;
1291
+ if ([type isEqualToString:@"_BUNDLE_"]) {
1292
+ if (*uriBundle == nil) {
1293
+ *uriBundle = [[NSBundle mainBundle] URLForResource:fileBundle withExtension:fileBundleExt];
1294
+ if (*uriBundle == nil) {
1295
+ // NSLog(@"RNInCallManager.getAudioUri(): %@.%@ not found in bundle.", fileBundle, fileBundleExt);
1296
+ type = fileSysWithExt;
1297
+ } else {
1298
+ return *uriBundle;
1299
+ }
1300
+ } else {
1301
+ return *uriBundle;
1302
+ }
1303
+ }
1304
+
1305
+ if (*uriDefault == nil) {
1306
+ NSString *target = [NSString stringWithFormat:@"%@/%@", fileSysPath, type];
1307
+ *uriDefault = [self getSysFileUri:target];
1308
+ }
1309
+ return *uriDefault;
1310
+ }
1311
+
1312
+ - (NSURL *)getSysFileUri:(NSString *)target
1313
+ {
1314
+ NSURL *url = [[NSURL alloc] initFileURLWithPath:target isDirectory:NO];
1315
+
1316
+ if (url != nil) {
1317
+ NSString *path = url.path;
1318
+ if (path != nil) {
1319
+ NSFileManager *fileManager = [[NSFileManager alloc] init];
1320
+ BOOL isTargetDirectory;
1321
+ if ([fileManager fileExistsAtPath:path isDirectory:&isTargetDirectory]) {
1322
+ if (!isTargetDirectory) {
1323
+ return url;
1324
+ }
1325
+ }
1326
+ }
1327
+ }
1328
+ // NSLog(@"RNInCallManager.getSysFileUri(): can not get url for %@", target);
1329
+ return nil;
1330
+ }
1331
+
1332
+ #pragma mark - AVAudioPlayerDelegate
1333
+
1334
+ // --- this only called when all loop played. it means, an infinite (numberOfLoops = -1) loop will never into here.
1335
+ - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player
1336
+ successfully:(BOOL)flag
1337
+ {
1338
+ NSString *filename = player.url.URLByDeletingPathExtension.lastPathComponent;
1339
+ // NSLog(@"RNInCallManager.audioPlayerDidFinishPlaying(): finished playing: %@", filename);
1340
+ if ([filename isEqualToString:_bundleBusytoneUri.URLByDeletingPathExtension.lastPathComponent]
1341
+ || [filename isEqualToString:_defaultBusytoneUri.URLByDeletingPathExtension.lastPathComponent]) {
1342
+ //[self stopBusytone];
1343
+ // NSLog(@"RNInCallManager.audioPlayerDidFinishPlaying(): busytone finished, invoke stop()");
1344
+ [self stop:@""];
1345
+ }
1346
+ }
1347
+
1348
+ - (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player
1349
+ error:(NSError *)error
1350
+ {
1351
+ NSString *filename = player.url.URLByDeletingPathExtension.lastPathComponent;
1352
+ // NSLog(@"RNInCallManager.audioPlayerDecodeErrorDidOccur(): player=%@, error=%@", filename, error.localizedDescription);
1353
+ }
1354
+ @end