@cometchat/calls-sdk-react-native 5.0.0-beta.1 → 5.0.0-beta.2

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.
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Copyright @ 2017-present 8x8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ #import "CometChatAudioSession.h"
18
+ #import <WebRTC/WebRTC.h>
19
+
20
+ @interface CometChatAudioSession (Private)
21
+
22
+ + (RTCAudioSession *)rtcAudioSession;
23
+
24
+ @end
@@ -0,0 +1,26 @@
1
+ /*
2
+ * Copyright @ 2017-present 8x8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ #import <Foundation/Foundation.h>
18
+
19
+ @class AVAudioSession;
20
+
21
+ @interface CometChatAudioSession : NSObject
22
+
23
+ + (void)activateWithAudioSession:(AVAudioSession *)session;
24
+ + (void)deactivateWithAudioSession:(AVAudioSession *)session;
25
+
26
+ @end
@@ -0,0 +1,35 @@
1
+ /*
2
+ * Copyright @ 2017-present 8x8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ #import "CometChatAudioSession.h"
18
+ #import "CometChatAudioSession+Private.h"
19
+
20
+
21
+ @implementation CometChatAudioSession
22
+
23
+ + (RTCAudioSession *)rtcAudioSession {
24
+ return [RTCAudioSession sharedInstance];
25
+ }
26
+
27
+ + (void)activateWithAudioSession:(AVAudioSession *)session {
28
+ [self.rtcAudioSession audioSessionDidActivate:session];
29
+ }
30
+
31
+ + (void)deactivateWithAudioSession:(AVAudioSession *)session {
32
+ [self.rtcAudioSession audioSessionDidDeactivate:session];
33
+ }
34
+
35
+ @end
@@ -0,0 +1,384 @@
1
+ /*
2
+ * Copyright @ 2017-present 8x8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ #import <AVFoundation/AVFoundation.h>
18
+
19
+ #import <React/RCTEventEmitter.h>
20
+ #import <React/RCTLog.h>
21
+ #import <WebRTC/WebRTC.h>
22
+
23
+ #import "CometChatAudioSession+Private.h"
24
+
25
+
26
+ // Audio mode
27
+ typedef enum {
28
+ kAudioModeDefault,
29
+ kAudioModeAudioCall,
30
+ kAudioModeVideoCall
31
+ } JitsiMeetAudioMode;
32
+
33
+ // Events
34
+ static NSString * const kDevicesChanged = @"org.jitsi.meet:features/audio-mode#devices-update";
35
+
36
+ // Device types (must match JS and Java)
37
+ static NSString * const kDeviceTypeBluetooth = @"BLUETOOTH";
38
+ static NSString * const kDeviceTypeCar = @"CAR";
39
+ static NSString * const kDeviceTypeEarpiece = @"EARPIECE";
40
+ static NSString * const kDeviceTypeHeadphones = @"HEADPHONES";
41
+ static NSString * const kDeviceTypeSpeaker = @"SPEAKER";
42
+ static NSString * const kDeviceTypeUnknown = @"UNKNOWN";
43
+
44
+
45
+ @interface AudioMode : RCTEventEmitter<RTCAudioSessionDelegate>
46
+
47
+ @property(nonatomic, strong) dispatch_queue_t workerQueue;
48
+
49
+ @end
50
+
51
+ @implementation AudioMode {
52
+ JitsiMeetAudioMode activeMode;
53
+ RTCAudioSessionConfiguration *defaultConfig;
54
+ RTCAudioSessionConfiguration *audioCallConfig;
55
+ RTCAudioSessionConfiguration *videoCallConfig;
56
+ RTCAudioSessionConfiguration *earpieceConfig;
57
+ BOOL forceSpeaker;
58
+ BOOL forceEarpiece;
59
+ BOOL isSpeakerOn;
60
+ BOOL isEarpieceOn;
61
+ }
62
+
63
+ RCT_EXPORT_MODULE();
64
+
65
+ + (BOOL)requiresMainQueueSetup {
66
+ return NO;
67
+ }
68
+
69
+ - (NSArray<NSString *> *)supportedEvents {
70
+ return @[ kDevicesChanged ];
71
+ }
72
+
73
+ - (NSDictionary *)constantsToExport {
74
+ return @{
75
+ @"DEVICE_CHANGE_EVENT": kDevicesChanged,
76
+ @"AUDIO_CALL" : [NSNumber numberWithInt: kAudioModeAudioCall],
77
+ @"DEFAULT" : [NSNumber numberWithInt: kAudioModeDefault],
78
+ @"VIDEO_CALL" : [NSNumber numberWithInt: kAudioModeVideoCall]
79
+ };
80
+ };
81
+
82
+ - (instancetype)init {
83
+ self = [super init];
84
+ if (self) {
85
+ dispatch_queue_attr_t attributes =
86
+ dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, -1);
87
+ _workerQueue = dispatch_queue_create("AudioMode.queue", attributes);
88
+
89
+ activeMode = kAudioModeDefault;
90
+
91
+ defaultConfig = [[RTCAudioSessionConfiguration alloc] init];
92
+ defaultConfig.category = AVAudioSessionCategoryAmbient;
93
+ defaultConfig.categoryOptions = 0;
94
+ defaultConfig.mode = AVAudioSessionModeDefault;
95
+
96
+ audioCallConfig = [[RTCAudioSessionConfiguration alloc] init];
97
+ audioCallConfig.category = AVAudioSessionCategoryPlayAndRecord;
98
+ audioCallConfig.categoryOptions = AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionDefaultToSpeaker;
99
+ audioCallConfig.mode = AVAudioSessionModeVoiceChat;
100
+
101
+ videoCallConfig = [[RTCAudioSessionConfiguration alloc] init];
102
+ videoCallConfig.category = AVAudioSessionCategoryPlayAndRecord;
103
+ videoCallConfig.categoryOptions = AVAudioSessionCategoryOptionAllowBluetooth;
104
+ videoCallConfig.mode = AVAudioSessionModeVideoChat;
105
+
106
+ // Manually routing audio to the earpiece doesn't quite work unless one disables BT (weird, I know).
107
+ earpieceConfig = [[RTCAudioSessionConfiguration alloc] init];
108
+ earpieceConfig.category = AVAudioSessionCategoryPlayAndRecord;
109
+ earpieceConfig.categoryOptions = 0;
110
+ earpieceConfig.mode = AVAudioSessionModeVoiceChat;
111
+
112
+ forceSpeaker = NO;
113
+ forceEarpiece = NO;
114
+ isSpeakerOn = NO;
115
+ isEarpieceOn = NO;
116
+
117
+ RTCAudioSession *session = CometChatAudioSession.rtcAudioSession;
118
+ [session addDelegate:self];
119
+ }
120
+
121
+ return self;
122
+ }
123
+
124
+ - (dispatch_queue_t)methodQueue {
125
+ // Use a dedicated queue for audio mode operations.
126
+ return _workerQueue;
127
+ }
128
+
129
+ - (BOOL)setConfigWithoutLock:(RTCAudioSessionConfiguration *)config
130
+ error:(NSError * _Nullable *)outError {
131
+ RTCAudioSession *session = CometChatAudioSession.rtcAudioSession;
132
+
133
+ return [session setConfiguration:config error:outError];
134
+ }
135
+
136
+ - (BOOL)setConfig:(RTCAudioSessionConfiguration *)config
137
+ error:(NSError * _Nullable *)outError {
138
+
139
+ RTCAudioSession *session = CometChatAudioSession.rtcAudioSession;
140
+ [session lockForConfiguration];
141
+ BOOL success = [self setConfigWithoutLock:config error:outError];
142
+ [session unlockForConfiguration];
143
+
144
+ return success;
145
+ }
146
+
147
+ #pragma mark - Exported methods
148
+
149
+ RCT_EXPORT_METHOD(setMode:(int)mode
150
+ resolve:(RCTPromiseResolveBlock)resolve
151
+ reject:(RCTPromiseRejectBlock)reject) {
152
+ RTCAudioSessionConfiguration *config = [self configForMode:mode];
153
+ NSError *error;
154
+
155
+ if (config == nil) {
156
+ reject(@"setMode", @"Invalid mode", nil);
157
+ return;
158
+ }
159
+
160
+ // Reset.
161
+ if (mode == kAudioModeDefault) {
162
+ forceSpeaker = NO;
163
+ forceEarpiece = NO;
164
+ }
165
+
166
+ activeMode = mode;
167
+
168
+ if ([self setConfig:config error:&error]) {
169
+ resolve(nil);
170
+ } else {
171
+ reject(@"setMode", error.localizedDescription, error);
172
+ }
173
+
174
+ [self notifyDevicesChanged];
175
+ }
176
+
177
+ RCT_EXPORT_METHOD(setAudioDevice:(NSString *)device
178
+ resolve:(RCTPromiseResolveBlock)resolve
179
+ reject:(RCTPromiseRejectBlock)reject) {
180
+ RCTLogInfo(@"[AudioMode] Selected device: %@", device);
181
+
182
+ RTCAudioSession *session = CometChatAudioSession.rtcAudioSession;
183
+ [session lockForConfiguration];
184
+ BOOL success;
185
+ NSError *error = nil;
186
+
187
+ // Reset these, as we are about to compute them.
188
+ forceSpeaker = NO;
189
+ forceEarpiece = NO;
190
+
191
+ // The speaker is special, so test for it first.
192
+ if ([device isEqualToString:kDeviceTypeSpeaker]) {
193
+ forceSpeaker = YES;
194
+ success = [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error];
195
+ } else {
196
+ // Here we use AVAudioSession because RTCAudioSession doesn't expose availableInputs.
197
+ AVAudioSession *_session = [AVAudioSession sharedInstance];
198
+ AVAudioSessionPortDescription *port = nil;
199
+
200
+ // Find the matching input device.
201
+ for (AVAudioSessionPortDescription *portDesc in _session.availableInputs) {
202
+ if ([portDesc.UID isEqualToString:device]) {
203
+ port = portDesc;
204
+ break;
205
+ }
206
+ }
207
+
208
+ if (port != nil) {
209
+ // First remove the override if we are going to select a different device.
210
+ if (isSpeakerOn) {
211
+ [session overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:nil];
212
+ }
213
+
214
+ // Special case for the earpiece.
215
+ if ([port.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {
216
+ forceEarpiece = YES;
217
+ [self setConfigWithoutLock:earpieceConfig error:nil];
218
+ } else if (isEarpieceOn) {
219
+ // Reset the config.
220
+ RTCAudioSessionConfiguration *config = [self configForMode:activeMode];
221
+ [self setConfigWithoutLock:config error:nil];
222
+ }
223
+
224
+ // Select our preferred input.
225
+ success = [session setPreferredInput:port error:&error];
226
+ } else {
227
+ success = NO;
228
+ error = RCTErrorWithMessage(@"Could not find audio device");
229
+ }
230
+ }
231
+
232
+ [session unlockForConfiguration];
233
+
234
+ if (success) {
235
+ resolve(nil);
236
+ } else {
237
+ reject(@"setAudioDevice", error != nil ? error.localizedDescription : @"", error);
238
+ }
239
+ }
240
+
241
+ RCT_EXPORT_METHOD(updateDeviceList) {
242
+ [self notifyDevicesChanged];
243
+ }
244
+
245
+ #pragma mark - RTCAudioSessionDelegate
246
+
247
+ - (void)audioSessionDidChangeRoute:(RTCAudioSession *)session
248
+ reason:(AVAudioSessionRouteChangeReason)reason
249
+ previousRoute:(AVAudioSessionRouteDescription *)previousRoute {
250
+ RCTLogInfo(@"[AudioMode] Route changed, reason: %lu", (unsigned long)reason);
251
+
252
+ // Update JS about the changes.
253
+ [self notifyDevicesChanged];
254
+
255
+ dispatch_async(_workerQueue, ^{
256
+ switch (reason) {
257
+ case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
258
+ case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
259
+ // If the device list changed, reset our overrides.
260
+ self->forceSpeaker = NO;
261
+ self->forceEarpiece = NO;
262
+ break;
263
+ case AVAudioSessionRouteChangeReasonCategoryChange:
264
+ // The category has changed, re-apply our config.
265
+ break;
266
+ default:
267
+ return;
268
+ }
269
+
270
+ // We don't want to touch the category when in default mode.
271
+ if (self->activeMode != kAudioModeDefault) {
272
+ RCTLogInfo(@"[AudioMode] Route changed, reapplying RTCAudioSession config");
273
+ RTCAudioSessionConfiguration *config = [self configForMode:self->activeMode];
274
+ [self setConfig:config error:nil];
275
+ if (self->forceSpeaker && !self->isSpeakerOn) {
276
+ [session lockForConfiguration];
277
+ [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
278
+ [session unlockForConfiguration];
279
+ }
280
+ }
281
+ });
282
+ }
283
+
284
+ - (void)audioSession:(RTCAudioSession *)audioSession didSetActive:(BOOL)active {
285
+ RCTLogInfo(@"[AudioMode] Audio session didSetActive:%d", active);
286
+ }
287
+
288
+ #pragma mark - Helper methods
289
+
290
+ - (RTCAudioSessionConfiguration *)configForMode:(int) mode {
291
+ if (mode != kAudioModeDefault && forceEarpiece) {
292
+ return earpieceConfig;
293
+ }
294
+
295
+ switch (mode) {
296
+ case kAudioModeAudioCall:
297
+ return audioCallConfig;
298
+ case kAudioModeDefault:
299
+ return defaultConfig;
300
+ case kAudioModeVideoCall:
301
+ return videoCallConfig;
302
+ default:
303
+ return nil;
304
+ }
305
+ }
306
+
307
+ // Here we convert input and output port types into a single type.
308
+ - (NSString *)portTypeToString:(AVAudioSessionPort) portType {
309
+ if ([portType isEqualToString:AVAudioSessionPortHeadphones]
310
+ || [portType isEqualToString:AVAudioSessionPortHeadsetMic]) {
311
+ return kDeviceTypeHeadphones;
312
+ } else if ([portType isEqualToString:AVAudioSessionPortBuiltInMic]
313
+ || [portType isEqualToString:AVAudioSessionPortBuiltInReceiver]) {
314
+ return kDeviceTypeEarpiece;
315
+ } else if ([portType isEqualToString:AVAudioSessionPortBuiltInSpeaker]) {
316
+ return kDeviceTypeSpeaker;
317
+ } else if ([portType isEqualToString:AVAudioSessionPortBluetoothHFP]
318
+ || [portType isEqualToString:AVAudioSessionPortBluetoothLE]
319
+ || [portType isEqualToString:AVAudioSessionPortBluetoothA2DP]) {
320
+ return kDeviceTypeBluetooth;
321
+ } else if ([portType isEqualToString:AVAudioSessionPortCarAudio]) {
322
+ return kDeviceTypeCar;
323
+ } else {
324
+ return kDeviceTypeUnknown;
325
+ }
326
+ }
327
+
328
+ - (void)notifyDevicesChanged {
329
+ dispatch_async(_workerQueue, ^{
330
+ NSMutableArray *data = [[NSMutableArray alloc] init];
331
+ AVAudioSession *session = [AVAudioSession sharedInstance];
332
+ NSString *currentPort = @"";
333
+ AVAudioSessionRouteDescription *currentRoute = session.currentRoute;
334
+
335
+ if (currentRoute != nil) {
336
+ AVAudioSessionPortDescription *output = currentRoute.outputs.firstObject;
337
+ AVAudioSessionPortDescription *input = currentRoute.inputs.firstObject;
338
+ if (output != nil && [output.portType isEqualToString:AVAudioSessionPortBuiltInSpeaker]) {
339
+ currentPort = kDeviceTypeSpeaker;
340
+ self->isSpeakerOn = YES;
341
+ } else if (input != nil) {
342
+ currentPort = input.UID;
343
+ self->isSpeakerOn = NO;
344
+ self->isEarpieceOn = [input.portType isEqualToString:AVAudioSessionPortBuiltInMic];
345
+ }
346
+ }
347
+
348
+ BOOL headphonesAvailable = NO;
349
+ for (AVAudioSessionPortDescription *portDesc in session.availableInputs) {
350
+ if ([portDesc.portType isEqualToString:AVAudioSessionPortHeadsetMic] || [portDesc.portType isEqualToString:AVAudioSessionPortHeadphones]) {
351
+ headphonesAvailable = YES;
352
+ break;
353
+ }
354
+ }
355
+
356
+ for (AVAudioSessionPortDescription *portDesc in session.availableInputs) {
357
+ // Skip "Phone" if headphones are present.
358
+ if (headphonesAvailable && [portDesc.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {
359
+ continue;
360
+ }
361
+ id deviceData
362
+ = @{
363
+ @"type": [self portTypeToString:portDesc.portType],
364
+ @"name": portDesc.portName,
365
+ @"uid": portDesc.UID,
366
+ @"selected": [NSNumber numberWithBool:[portDesc.UID isEqualToString:currentPort]]
367
+ };
368
+ [data addObject:deviceData];
369
+ }
370
+
371
+ // We need to manually add the speaker because it will never show up in the
372
+ // previous list, as it's not an input.
373
+ [data addObject:
374
+ @{ @"type": kDeviceTypeSpeaker,
375
+ @"name": @"Speaker",
376
+ @"uid": kDeviceTypeSpeaker,
377
+ @"selected": [NSNumber numberWithBool:[kDeviceTypeSpeaker isEqualToString:currentPort]]
378
+ }];
379
+
380
+ [self sendEventWithName:kDevicesChanged body:data];
381
+ });
382
+ }
383
+
384
+ @end
@@ -0,0 +1,4 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ @interface KeepAwakeModule : NSObject <RCTBridgeModule>
4
+ @end
@@ -0,0 +1,29 @@
1
+ #import "KeepAwake.h"
2
+ #import <UIKit/UIKit.h>
3
+
4
+ #import <React/RCTBridgeModule.h>
5
+ #import <React/RCTLog.h>
6
+
7
+ @implementation KeepAwakeModule
8
+
9
+ RCT_EXPORT_MODULE();
10
+
11
+ - (dispatch_queue_t)methodQueue {
12
+ return dispatch_get_main_queue();
13
+ }
14
+
15
+ RCT_EXPORT_METHOD(activateKeepAwake) {
16
+ dispatch_async(dispatch_get_main_queue(), ^{
17
+ [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
18
+ RCTLogInfo(@"Keep awake activated");
19
+ });
20
+ }
21
+
22
+ RCT_EXPORT_METHOD(deactivateKeepAwake) {
23
+ dispatch_async(dispatch_get_main_queue(), ^{
24
+ [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
25
+ RCTLogInfo(@"Keep awake deactivated");
26
+ });
27
+ }
28
+
29
+ @end
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "@cometchat/calls-sdk-react-native",
3
- "version": "5.0.0-beta.1",
3
+ "version": "5.0.0-beta.2",
4
+ "description": "CometChat Calls SDK for React Native provides voice and video calling capabilities for React Native applications.",
4
5
  "private": false,
5
6
  "main": "dist/index.js",
6
7
  "module": "dist/index.mjs",
7
8
  "types": "dist/index.d.mts",
8
9
  "files": [
9
10
  "dist",
10
- "android"
11
+ "android",
12
+ "ios",
13
+ "cometchat-calls-sdk-react-native.podspec"
11
14
  ],
12
15
  "scripts": {},
13
16
  "dependencies": {},
@@ -23,5 +26,9 @@
23
26
  },
24
27
  "engines": {
25
28
  "node": ">=18"
26
- }
29
+ },
30
+ "repository": "https://github.com/cometchat/calls-sdk-react-native",
31
+ "author": "Cometchat Team (https://cometchat.com)",
32
+ "license": "https://www.cometchat.com/legal-terms-of-service",
33
+ "homepage": "https://www.cometchat.com/docs/sdk/react-native/calling-overview"
27
34
  }