edoctor-sendbird-calls 1.0.0

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 (71) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +33 -0
  3. package/android/build.gradle +82 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifest.xml +34 -0
  6. package/android/src/main/AndroidManifestNew.xml +2 -0
  7. package/android/src/main/java/com/edoctorsendbirdcalls/ComingCallActivity.java +41 -0
  8. package/android/src/main/java/com/edoctorsendbirdcalls/RNSendBirdCallsModule.java +2068 -0
  9. package/android/src/main/java/com/edoctorsendbirdcalls/RNSendBirdCallsPackage.java +32 -0
  10. package/android/src/main/java/com/edoctorsendbirdcalls/RNVSendBirdCallsVideo.java +10 -0
  11. package/android/src/main/java/com/edoctorsendbirdcalls/RNVSendBirdCallsVideoManager.java +77 -0
  12. package/android/src/main/java/com/edoctorsendbirdcalls/RNVideoViewGroup.java +109 -0
  13. package/android/src/main/java/com/edoctorsendbirdcalls/SharedUtils.java +127 -0
  14. package/android/src/main/java/com/edoctorsendbirdcalls/VcCallReceiver.java +37 -0
  15. package/android/src/main/java/com/edoctorsendbirdcalls/VcHeadlessService.java +23 -0
  16. package/android/src/main/java/com/edoctorsendbirdcalls/VoIPBaseService.java +100 -0
  17. package/android/src/main/java/com/edoctorsendbirdcalls/VoIPService.java +164 -0
  18. package/android/src/main/java/com/edoctorsendbirdcalls/XiaomiUtilities.java +75 -0
  19. package/android/src/main/res/drawable/bar_selector_white.xml +18 -0
  20. package/android/src/main/res/drawable/button_answer.xml +8 -0
  21. package/android/src/main/res/drawable/button_reject.xml +8 -0
  22. package/android/src/main/res/drawable/call_notification_line.xml +8 -0
  23. package/android/src/main/res/drawable/ic_answer.xml +5 -0
  24. package/android/src/main/res/drawable/ic_call_notification_answer.xml +6 -0
  25. package/android/src/main/res/drawable/ic_call_notification_decline.xml +9 -0
  26. package/android/src/main/res/drawable/ic_reject.xml +5 -0
  27. package/android/src/main/res/drawable/rn_edit_text_material.xml +36 -0
  28. package/android/src/main/res/layout/call_notification.xml +124 -0
  29. package/edoctor-sendbird-calls.podspec +27 -0
  30. package/ios/CXCallManager.swift +211 -0
  31. package/ios/CXProvider.swift +38 -0
  32. package/ios/Extensions/UIKit/UIColor.swift +30 -0
  33. package/ios/Extensions/UIKit/UIView+Extension.swift +61 -0
  34. package/ios/Extensions/UIKit/UIViewController.swift +28 -0
  35. package/ios/PrivacyInfo.xcprivacy +47 -0
  36. package/ios/RNSendBirdCalls-Bridging-Header.h +3 -0
  37. package/ios/RNSendBirdCalls.mm +94 -0
  38. package/ios/RNSendBirdCalls.swift +549 -0
  39. package/ios/RNSendBirdCallsHelper.swift +28 -0
  40. package/ios/RNVSendBirdCallsVideo.swift +55 -0
  41. package/ios/RNVSendBirdCallsVideoManager.mm +6 -0
  42. package/ios/RNVSendBirdCallsVideoManager.swift +15 -0
  43. package/ios/SendBirdCallsExtentions.swift +46 -0
  44. package/lib/module/SendBirdCalls.js +377 -0
  45. package/lib/module/SendBirdCalls.js.map +1 -0
  46. package/lib/module/SendBirdCallsEvents.js +108 -0
  47. package/lib/module/SendBirdCallsEvents.js.map +1 -0
  48. package/lib/module/SendBirdCallsVideo.js +33 -0
  49. package/lib/module/SendBirdCallsVideo.js.map +1 -0
  50. package/lib/module/index.js +14 -0
  51. package/lib/module/index.js.map +1 -0
  52. package/lib/module/index.type.js +2 -0
  53. package/lib/module/index.type.js.map +1 -0
  54. package/lib/module/package.json +1 -0
  55. package/lib/typescript/package.json +1 -0
  56. package/lib/typescript/src/SendBirdCalls.d.ts +63 -0
  57. package/lib/typescript/src/SendBirdCalls.d.ts.map +1 -0
  58. package/lib/typescript/src/SendBirdCallsEvents.d.ts +36 -0
  59. package/lib/typescript/src/SendBirdCallsEvents.d.ts.map +1 -0
  60. package/lib/typescript/src/SendBirdCallsVideo.d.ts +18 -0
  61. package/lib/typescript/src/SendBirdCallsVideo.d.ts.map +1 -0
  62. package/lib/typescript/src/index.d.ts +5 -0
  63. package/lib/typescript/src/index.d.ts.map +1 -0
  64. package/lib/typescript/src/index.type.d.ts +14 -0
  65. package/lib/typescript/src/index.type.d.ts.map +1 -0
  66. package/package.json +154 -0
  67. package/src/SendBirdCalls.ts +472 -0
  68. package/src/SendBirdCallsEvents.ts +141 -0
  69. package/src/SendBirdCallsVideo.tsx +44 -0
  70. package/src/index.tsx +11 -0
  71. package/src/index.type.ts +20 -0
@@ -0,0 +1,549 @@
1
+ import Foundation
2
+ import CallKit
3
+ import PushKit
4
+ import SendBirdCalls
5
+ import UIKit
6
+ import React
7
+ import AVFoundation
8
+
9
+ @objc(RNSendBirdCalls)
10
+ class RNSendBirdCalls: RCTEventEmitter, SendBirdCallDelegate, DirectCallDelegate, PKPushRegistryDelegate, SendBirdRecordingDelegate {
11
+
12
+ var queue: DispatchQueue = DispatchQueue(label: "RNSendBirdCalls")
13
+ var voipPushRegistry: PKPushRegistry?
14
+ var HAS_LISTENER = false
15
+ var CALL_ACTIVE: DirectCall?
16
+
17
+ let TAG = "[RNSendBirdCalls]_"
18
+
19
+ static let onEnded = "onEnded"
20
+ static let onRinging = "onRinging"
21
+ static let onConnected = "onConnected"
22
+ static let onReconnected = "onReconnected"
23
+ static let onReconnecting = "onReconnecting"
24
+ static let onCallDidAccept = "onCallDidAccept"
25
+ static let onRemoteAudioSettingsChanged = "onRemoteAudioSettingsChanged"
26
+ static let onRemoteVideoSettingsChanged = "onRemoteVideoSettingsChanged"
27
+ static let onRecordingSucceeded = "onRecordingSucceeded"
28
+ static let onRecordingFailed = "onRecordingFailed"
29
+
30
+ let jsEvents: NSMutableArray = []
31
+
32
+ override init() {
33
+ super.init()
34
+ setupSendBird()
35
+ }
36
+
37
+ deinit {
38
+ removeAllEvents()
39
+ voipPushRegistry?.delegate = nil
40
+ voipPushRegistry = nil
41
+ CALL_ACTIVE?.delegate = nil
42
+ CALL_ACTIVE = nil
43
+ }
44
+
45
+ private func setupSendBird() {
46
+ let userDefaults = UserDefaults(suiteName: "edoctorStorage")
47
+ let appId = "0BEF9C57-BA3B-474E-A40F-62B027AA47F6"
48
+
49
+ let config = SendBirdCall.configure(appId: appId)
50
+ if(config){
51
+ print(TAG + "CONFIG SUCCESS")
52
+ }else {
53
+ print(TAG + "CONFIG FAILED")
54
+ }
55
+ SendBirdCall.addDelegate(self, identifier: "RNSendBirdCalls")
56
+ SendBirdCall.setDirectCallDialingSoundOnWhenSilentMode(isEnabled: true)
57
+ SendBirdCall.addRecordingDelegate(self, identifier: "RNSendBirdRecording")
58
+ }
59
+
60
+ // MARK: - RCTEventEmitter overrides for RN 0.79.x
61
+ override static func requiresMainQueueSetup() -> Bool {
62
+ return true
63
+ }
64
+
65
+ override func supportedEvents() -> [String]! {
66
+ return [
67
+ RNSendBirdCalls.onEnded,
68
+ RNSendBirdCalls.onRinging,
69
+ RNSendBirdCalls.onConnected,
70
+ RNSendBirdCalls.onReconnected,
71
+ RNSendBirdCalls.onReconnecting,
72
+ RNSendBirdCalls.onCallDidAccept,
73
+ RNSendBirdCalls.onRemoteAudioSettingsChanged,
74
+ RNSendBirdCalls.onRemoteVideoSettingsChanged,
75
+ RNSendBirdCalls.onRecordingSucceeded,
76
+ RNSendBirdCalls.onRecordingFailed,
77
+ ]
78
+ }
79
+
80
+ override func startObserving() {
81
+ HAS_LISTENER = true
82
+ }
83
+
84
+ override func stopObserving() {
85
+ HAS_LISTENER = false
86
+ }
87
+
88
+ @objc func setNativeEvent(_ event: String) {
89
+ DispatchQueue.main.async {
90
+ if !self.jsEvents.contains(event) {
91
+ self.jsEvents.add(event)
92
+ }
93
+ }
94
+ }
95
+
96
+ @objc func removeNativeEvent(_ event: String) {
97
+ DispatchQueue.main.async {
98
+ let index = self.jsEvents.index(of: event)
99
+ if index != NSNotFound {
100
+ self.jsEvents.removeObject(at: index)
101
+ }
102
+ }
103
+ }
104
+
105
+ @objc func removeAllEvents() {
106
+ DispatchQueue.main.async {
107
+ self.jsEvents.removeAllObjects()
108
+ }
109
+ SendBirdCall.removeAllDelegates()
110
+ SendBirdCall.removeAllRecordingDelegates()
111
+ }
112
+
113
+ // MARK: - SendBird Methods
114
+ @objc func shareStorage(
115
+ _ appId: String
116
+ ) -> Void {
117
+ let userDefaults = UserDefaults(suiteName: "edoctorStorage")
118
+ userDefaults?.set(appId, forKey: "APP_ID_NATIVE")
119
+ userDefaults?.synchronize()
120
+ }
121
+
122
+ @objc func authenticate(_ userId: String, accessToken: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
123
+ let params = AuthenticateParams(userId: userId, accessToken: accessToken)
124
+
125
+ SendBirdCall.authenticate(with: params) { (user, error) in
126
+ guard let user = user, error == nil else {
127
+ let code = "\(error?.errorCode.rawValue ?? 0)"
128
+ let message = error?.localizedDescription ?? "Có lỗi xảy ra"
129
+ resolve(["succeeded": false , "errorCode": code, "errorMessage": message])
130
+ return
131
+ }
132
+
133
+ resolve(["succeeded": true ,"userId": user.userId, "nickname": user.nickname ?? ""])
134
+ }
135
+ }
136
+
137
+ @objc func deAuthenticate(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
138
+ SendBirdCall.unregisterAllVoIPPushTokens { error in
139
+ guard error == nil else {
140
+ print("RNSendBirdCalls" + "Cerror deauthen")
141
+ let message = error?.localizedDescription ?? "Có lỗi xảy ra"
142
+ print("RNSendBirdCalls" + message)
143
+ return
144
+ }
145
+ }
146
+ SendBirdCall.deauthenticate { (error) in
147
+ guard error == nil else {
148
+ let code = "\(error?.errorCode.rawValue ?? 0)"
149
+ let message = error?.localizedDescription ?? "Có lỗi xảy ra"
150
+ resolve(["succeeded": false , "errorCode": code, "errorMessage": message])
151
+ return
152
+ }
153
+
154
+ resolve(["succeeded": true ])
155
+ }
156
+ }
157
+
158
+ @objc func initSendBird(_ appId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
159
+ let config = SendBirdCall.configure(appId: appId)
160
+ if(config){
161
+ resolve(["succeeded": true ])
162
+ }else {
163
+ resolve(["succeeded": false ])
164
+ }
165
+ }
166
+
167
+ @objc func dial(_ calleeId: String, isVideoCall: Bool, isVideoEnable: Bool, isAudioEnable: Bool, appointmentScheduleId: String, eClinicId: String , resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock){
168
+ print(TAG + "DIAL")
169
+ let callOptions = CallOptions(isAudioEnabled: isAudioEnable, isVideoEnabled: isVideoEnable, useFrontCamera: true)
170
+ let customItems = [
171
+ "appointmentScheduleId": appointmentScheduleId,
172
+ "eClinicId": eClinicId
173
+ ]
174
+ let params = DialParams(calleeId: calleeId, isVideoCall: isVideoCall, callOptions: callOptions, customItems: customItems)
175
+
176
+ let directCall = SendBirdCall.dial(with: params) { call, error in
177
+ guard let call = call, error == nil else {
178
+ let code = "\(error?.errorCode.rawValue ?? 0)"
179
+ let message = error?.localizedDescription ?? "Có lỗi xảy ra"
180
+ resolve(["succeeded": false , "errorCode": code, "errorMessage": message])
181
+ return
182
+ }
183
+
184
+ resolve(["succeeded": true ,"callId": call.callId, "caller": call.caller?.userId ?? "", "callee": call.callee?.userId ?? ""])
185
+ }
186
+ CALL_ACTIVE = directCall;
187
+ directCall?.delegate = self
188
+ }
189
+
190
+ @objc func endCall(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
191
+ print(TAG + "END CALL")
192
+ guard let call = CALL_ACTIVE else {
193
+ resolve(["succeeded": false , "errorMessage": "Cuộc gọi không tồn tại"])
194
+ return
195
+ }
196
+ call.end()
197
+ CXCallManager.shared.endCXCall(call)
198
+ let params = getParamFromDirectCall(call: call)
199
+ params.setValue(true, forKey: "succeeded")
200
+ resolve(params)
201
+ }
202
+
203
+ @objc func acceptCall(_ callId: String, isVideoEnable: Bool, isAudioEnable: Bool, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
204
+ print(TAG + "ACCEPT CALL")
205
+ guard let call = SendBirdCall.getCall(forCallId: callId) else {
206
+ resolve(["succeeded": false , "errorMessage": "Cuộc gọi không tồn tại"])
207
+ return
208
+ }
209
+ let callOptions = CallOptions(isAudioEnabled: isAudioEnable, isVideoEnabled: isVideoEnable, useFrontCamera: true)
210
+ let acceptParams = AcceptParams(callOptions: callOptions)
211
+ call.accept(with: acceptParams)
212
+ let params = getParamFromDirectCall(call: call)
213
+ params.setValue(true, forKey: "succeeded")
214
+ resolve(params)
215
+ }
216
+
217
+ @objc func voipRegistry() {
218
+ self.voipPushRegistry = PKPushRegistry(queue: DispatchQueue.main)
219
+ self.voipPushRegistry?.delegate = self
220
+ self.voipPushRegistry?.desiredPushTypes = [.voIP]
221
+ }
222
+
223
+ @objc func addDirectCallSound(_ soundType: String, filename: String) {
224
+ if(soundType == "DIALING"){
225
+ SendBirdCall.addDirectCallSound(filename, forType: .dialing)
226
+ }else if(soundType == "RINGING"){
227
+ SendBirdCall.addDirectCallSound(filename, forType: .ringing)
228
+ }else if(soundType == "RECONNECTING"){
229
+ SendBirdCall.addDirectCallSound(filename, forType: .reconnecting)
230
+ }else if(soundType == "RECONNECTED"){
231
+ SendBirdCall.addDirectCallSound(filename, forType: .reconnected)
232
+ }
233
+ }
234
+
235
+ @objc func setDirectCallDialingSoundOnWhenSilentMode(_ isEnabled: Bool) {
236
+ SendBirdCall.setDirectCallDialingSoundOnWhenSilentMode(isEnabled: isEnabled)
237
+ }
238
+
239
+ @objc func getOngoingCalls(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
240
+ let ongoingCalls = SendBirdCall.getOngoingCalls()
241
+ var resultData: [NSDictionary] = []
242
+
243
+ if(ongoingCalls.count > 0){
244
+ ongoingCalls.forEach({ directCall in
245
+ resultData.append(getParamFromDirectCall(call: directCall))
246
+ })
247
+ }
248
+ resolve(resultData)
249
+ }
250
+
251
+ @objc func muteMicrophone(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
252
+ print(TAG + "muteMicrophone")
253
+ if(CALL_ACTIVE != nil){
254
+ CALL_ACTIVE?.muteMicrophone()
255
+ resolve(["succeeded": true])
256
+ }else {
257
+ resolve(["succeeded": false , "errorMessage": "Cuộc gọi không tồn tại"])
258
+ }
259
+ }
260
+
261
+ @objc func unmuteMicrophone(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
262
+ print(TAG + "unmuteMicrophone")
263
+ if(CALL_ACTIVE != nil){
264
+ CALL_ACTIVE?.unmuteMicrophone()
265
+ resolve(["succeeded": true])
266
+ }else {
267
+ resolve(["succeeded": false , "errorMessage": "Cuộc gọi không tồn tại"])
268
+ }
269
+ }
270
+
271
+ @objc func startVideo(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
272
+ print(TAG + "startVideo")
273
+ if(CALL_ACTIVE != nil){
274
+ CALL_ACTIVE?.startVideo()
275
+ resolve(["succeeded": true])
276
+ }else {
277
+ resolve(["succeeded": false , "errorMessage": "Cuộc gọi không tồn tại"])
278
+ }
279
+ }
280
+
281
+ @objc func stopVideo(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
282
+ print(TAG + "stopVideo")
283
+ if(CALL_ACTIVE != nil){
284
+ CALL_ACTIVE?.stopVideo()
285
+ resolve(["succeeded": true])
286
+ }else {
287
+ resolve(["succeeded": false , "errorMessage": "Cuộc gọi không tồn tại"])
288
+ }
289
+ }
290
+
291
+ @objc func switchCamera(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
292
+ print(TAG + "switchCamera")
293
+ if(CALL_ACTIVE != nil){
294
+ CALL_ACTIVE?.switchCamera { error in
295
+ if(error != nil){
296
+ let code = "\(error?.errorCode.rawValue ?? 0)"
297
+ let message = error?.localizedDescription ?? "Có lỗi xảy ra"
298
+ resolve(["succeeded": false , "errorCode": code, "errorMessage": message])
299
+ }else {
300
+ resolve(["succeeded": true])
301
+ }
302
+ }
303
+ } else {
304
+ resolve(["succeeded": false , "errorMessage": "Cuộc gọi không tồn tại"])
305
+ }
306
+ }
307
+
308
+ @objc func startRecording(_ fileName: String, isRemote: Bool, resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
309
+ print(TAG + "startRecording")
310
+
311
+ let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
312
+ let documentsDirectory = paths[0]
313
+
314
+ let options = isRemote ? RecordingOptions(recordingType: .localAudioRemoteAudioAndVideo, directoryPath: documentsDirectory, fileName: fileName) : RecordingOptions(recordingType: .localAudioAndVideoRemoteAudio, directoryPath: documentsDirectory, fileName: fileName)
315
+
316
+ if(CALL_ACTIVE != nil){
317
+ CALL_ACTIVE?.startRecording(options: options) { (recordingId, error) in
318
+ if(error == nil){
319
+ let result: NSDictionary = [
320
+ "succeeded": true,
321
+ "recordingId": recordingId ?? "",
322
+ "filePath": documentsDirectory.description,
323
+ ]
324
+ resolve(result)
325
+ }else {
326
+ let result: NSDictionary = [
327
+ "succeeded": false,
328
+ "recordingId": recordingId ?? "",
329
+ ]
330
+ resolve(result)
331
+ }
332
+ }
333
+ }else {
334
+ let result: NSDictionary = [
335
+ "succeeded": false,
336
+ ]
337
+ resolve(result)
338
+ }
339
+ }
340
+
341
+ @objc func stopRecording(_ recordingId: String, isVideoCall: Bool, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock){
342
+ print(TAG + "stopRecording")
343
+ if(CALL_ACTIVE != nil){
344
+ let resultStop: Bool = ((CALL_ACTIVE?.stopRecording(recordingId: recordingId)) != nil)
345
+ let result: NSDictionary = [
346
+ "succeeded": resultStop,
347
+ ]
348
+ resolve(result)
349
+ }else {
350
+ let result: NSDictionary = [
351
+ "succeeded": false,
352
+ ]
353
+ resolve(result)
354
+ }
355
+ }
356
+
357
+ @objc func endAllCalls(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
358
+ let ongoingCalls = SendBirdCall.getOngoingCalls()
359
+ if(ongoingCalls.count > 0){
360
+ ongoingCalls.forEach({ directCall in
361
+ directCall.end()
362
+ CXCallManager.shared.endCXCall(directCall)
363
+ })
364
+ }
365
+ resolve(["succeeded": true])
366
+ }
367
+
368
+ func endCalls() {
369
+ let ongoingCalls = SendBirdCall.getOngoingCalls()
370
+ if(ongoingCalls.count > 0){
371
+ ongoingCalls.forEach({ directCall in
372
+ directCall.end()
373
+ CXCallManager.shared.endCXCall(directCall)
374
+ })
375
+ }
376
+ }
377
+
378
+ // MARK: - PKPushRegistryDelegate
379
+ func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
380
+ SendBirdCall.registerVoIPPush(token: pushCredentials.token, unique: true) { error in
381
+ guard error == nil else { return }
382
+ }
383
+ }
384
+
385
+ func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
386
+ print(TAG + "pushRegistry incoming push event")
387
+ SendBirdCall.pushRegistry(registry, didReceiveIncomingPushWith: payload, for: type, completionHandler: nil)
388
+ }
389
+
390
+ @available(iOS 10.0, *)
391
+ func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
392
+ print(TAG + "pushRegistry pushRegistry incoming call")
393
+ SendBirdCall.addDelegate(self, identifier: "RNSendBirdCalls")
394
+ SendBirdCall.pushRegistry(registry, didReceiveIncomingPushWith: payload, for: type) { uuid in
395
+ guard uuid != nil else {
396
+ let update = CXCallUpdate()
397
+ update.remoteHandle = CXHandle(type: .generic, value: "invalid")
398
+ let randomUUID = UUID()
399
+
400
+ CXCallManager.shared.reportIncomingCall(with: randomUUID, update: update) { _ in
401
+ CXCallManager.shared.endCall(for: randomUUID, endedAt: Date(), reason: .acceptFailed)
402
+ }
403
+ completion()
404
+ return
405
+ }
406
+ completion()
407
+ }
408
+ }
409
+
410
+ // MARK: - Helper Methods
411
+ func getParamFromDirectCall(call: DirectCall) -> NSMutableDictionary {
412
+ let calleeMap: NSDictionary = [
413
+ "userId": call.callee?.userId ?? "",
414
+ "nickname": call.callee?.nickname ?? "",
415
+ "profileURL": call.callee?.profileURL ?? "",
416
+ ]
417
+
418
+ let callerMap: NSDictionary = [
419
+ "userId": call.caller?.userId ?? "",
420
+ "nickname": call.caller?.nickname ?? "",
421
+ "profileURL": call.caller?.profileURL ?? "",
422
+ ]
423
+
424
+ let callId = call.callId
425
+ let isVideoCall = call.isVideoCall
426
+ let duration: Int64 = call.duration
427
+ let isRemoteAudioEnabled = call.isRemoteAudioEnabled
428
+ let isRemoteVideoEnabled = call.isRemoteVideoEnabled
429
+ let isLocalVideoEnabled = call.isLocalVideoEnabled
430
+ let isLocalAudioEnabled = call.isLocalAudioEnabled
431
+ let customItems = call.customItems
432
+
433
+ let params: NSMutableDictionary = [
434
+ "callId" : callId ?? "",
435
+ "duration": duration,
436
+ "isRemoteAudioEnabled": isRemoteAudioEnabled,
437
+ "isRemoteVideoEnabled": isRemoteVideoEnabled,
438
+ "isLocalVideoEnabled": isLocalVideoEnabled,
439
+ "isLocalAudioEnabled": isLocalAudioEnabled,
440
+ "isVideoCall": isVideoCall,
441
+ "callee" : calleeMap,
442
+ "caller" : callerMap,
443
+ "customItems": customItems ?? [:],
444
+ ]
445
+ return params
446
+ }
447
+
448
+ func sendEventWithName(eventName: String, params: NSDictionary) {
449
+ // Ensure we're on main queue for RN 0.79.x
450
+ DispatchQueue.main.async {
451
+ if self.jsEvents.contains(eventName) && self.HAS_LISTENER && self.bridge != nil {
452
+ self.sendEvent(withName: eventName, body: params)
453
+ }
454
+ }
455
+ }
456
+
457
+ // MARK: - SendBirdCallDelegate
458
+ func didStartRinging(_ directCall: DirectCall) {
459
+ print(TAG + "onRinging")
460
+ directCall.delegate = self
461
+ CALL_ACTIVE = directCall
462
+
463
+ guard let uuid = directCall.callUUID else { return }
464
+ guard CXCallManager.shared.shouldProcessCall(for: uuid) else { return }
465
+
466
+ let name = "BS. \(directCall.caller?.nickname ?? "Chưa xác định")"
467
+ let update = CXCallUpdate()
468
+ update.remoteHandle = CXHandle(type: .generic, value: name)
469
+ update.hasVideo = directCall.isVideoCall
470
+ update.localizedCallerName = "BS. \(directCall.caller?.nickname ?? "Chưa xác định")"
471
+
472
+ if SendBirdCall.getOngoingCallCount() > 1 {
473
+ CXCallManager.shared.reportIncomingCall(with: uuid, update: update) { _ in
474
+ CXCallManager.shared.endCall(for: uuid, endedAt: Date(), reason: .declined)
475
+ }
476
+ directCall.end()
477
+ } else {
478
+ CXCallManager.shared.reportIncomingCall(with: uuid, update: update)
479
+ let params = getParamFromDirectCall(call: directCall)
480
+ sendEventWithName(eventName: RNSendBirdCalls.onRinging, params: params)
481
+ }
482
+ }
483
+
484
+ func didEstablish(_ directCall: DirectCall) {
485
+ print(TAG + "onCallDidAccept")
486
+ let params = getParamFromDirectCall(call: directCall)
487
+ sendEventWithName(eventName: RNSendBirdCalls.onCallDidAccept, params: params)
488
+ }
489
+
490
+ func didConnect(_ directCall: DirectCall) {
491
+ print(TAG + "onConnected")
492
+ let params = getParamFromDirectCall(call: directCall)
493
+ sendEventWithName(eventName: RNSendBirdCalls.onConnected, params: params)
494
+ }
495
+
496
+ func didStartReconnecting(_ directCall: DirectCall) {
497
+ print(TAG + "onReconnecting")
498
+ let params = getParamFromDirectCall(call: directCall)
499
+ sendEventWithName(eventName: RNSendBirdCalls.onReconnecting, params: params)
500
+ }
501
+
502
+ func didReconnect(_ directCall: DirectCall) {
503
+ print(TAG + "didReconnect")
504
+ let params = getParamFromDirectCall(call: directCall)
505
+ sendEventWithName(eventName: RNSendBirdCalls.onReconnected, params: params)
506
+ }
507
+
508
+ func didRemoteAudioSettingsChange(_ directCall: DirectCall) {
509
+ print(TAG + "onRemoteAudioSettingsChanged")
510
+ let params = getParamFromDirectCall(call: directCall)
511
+ sendEventWithName(eventName: RNSendBirdCalls.onRemoteAudioSettingsChanged, params: params)
512
+ }
513
+
514
+ func didRemoteVideoSettingsChange(_ directCall: DirectCall) {
515
+ print(TAG + "onRemoteVideoSettingsChanged")
516
+ let params = getParamFromDirectCall(call: directCall)
517
+ sendEventWithName(eventName: RNSendBirdCalls.onRemoteVideoSettingsChanged, params: params)
518
+ }
519
+
520
+ func didEnd(_ directCall: DirectCall) {
521
+ var callId: UUID = UUID()
522
+ if let callUUID = directCall.callUUID {
523
+ callId = callUUID
524
+ }
525
+ print(TAG + "didEnd")
526
+ CALL_ACTIVE = nil
527
+ let params = getParamFromDirectCall(call: directCall)
528
+ sendEventWithName(eventName: RNSendBirdCalls.onEnded, params: params)
529
+ CXCallManager.shared.endCall(for: callId, endedAt: Date(), reason: directCall.endResult)
530
+ guard let callLog = directCall.callLog else { return }
531
+ }
532
+
533
+ // MARK: - SendBirdRecordingDelegate
534
+ func didSaveRecording(call: DirectCall, recordingId: String, options: RecordingOptions, outputURL: URL) {
535
+ print(TAG + "didSaveRecording")
536
+ let recordingMap: NSDictionary = [
537
+ "outputFilePath": outputURL.absoluteString,
538
+ ]
539
+ sendEventWithName(eventName: RNSendBirdCalls.onRecordingSucceeded, params: recordingMap)
540
+ }
541
+
542
+ func didFailToSaveRecording(call: DirectCall, recordingId: String, error: SBCError) {
543
+ print(TAG + "didFailToSaveRecording")
544
+ let recordingMap: NSDictionary = [
545
+ "errorMessage": error.localizedDescription,
546
+ ]
547
+ sendEventWithName(eventName: RNSendBirdCalls.onRecordingFailed, params: recordingMap)
548
+ }
549
+ }
@@ -0,0 +1,28 @@
1
+ import SendBirdCalls
2
+ @objc(RNSendBirdCallsHelper)
3
+ public class RNSendBirdCallsHelper: NSObject {
4
+ @objc public static func applicationWillTerminate(application: UIApplication?) -> Void {
5
+ print("sayHello2")
6
+
7
+ // This method will be called when the app is forcefully terminated.
8
+ // End all ongoing calls in this method.
9
+ let callManager = CXCallManager.shared
10
+ let ongoingCalls = callManager.currentCalls.compactMap { SendBirdCall.getCall(forUUID: $0.uuid) }
11
+
12
+ ongoingCalls.forEach { directCall in
13
+ // Sendbird Calls: End call
14
+ directCall.end()
15
+
16
+ // CallKit: Request End transaction
17
+ callManager.endCXCall(directCall)
18
+
19
+ // CallKit: Report End if uuid is valid
20
+ if let uuid = directCall.callUUID {
21
+ callManager.endCall(for: uuid, endedAt: Date(), reason: .none)
22
+ }
23
+ }
24
+ // However, because iOS gives a limited time to perform remaining tasks,
25
+ // There might be some calls failed to be ended
26
+ // In this case, I recommend that you register local notification to notify the unterminated calls.
27
+ }
28
+ }
@@ -0,0 +1,55 @@
1
+ import SendBirdCalls
2
+ import CoreGraphics
3
+
4
+ @objc(RNVSendBirdCallsVideo)
5
+ class RNVSendBirdCallsVideo : UIView {
6
+ @objc var callId: String = ""
7
+ @objc var local: Bool = true
8
+ @objc var scaleFit: Bool = true
9
+ var videoView: SendBirdVideoView!
10
+
11
+ override init(frame: CGRect) {
12
+ super.init(frame: frame);
13
+ videoView = SendBirdVideoView(frame: self.frame)
14
+ videoView.backgroundColor = UIColor.gray
15
+ videoView.frame = self.bounds
16
+ self.addSubview(videoView)
17
+ }
18
+ required init?(coder aDecoder: NSCoder) {
19
+ fatalError("init(coder:) has not been implemented")
20
+ }
21
+
22
+ override func layoutSubviews() {
23
+ videoView.frame = self.bounds
24
+ }
25
+
26
+ override func didSetProps(_ changedProps: [String]!) {
27
+ guard let call = SendBirdCall.getCall(forCallId: self.callId) else {
28
+ return
29
+ }
30
+
31
+ if (self.scaleFit) {
32
+ self.videoView.videoContentMode = UIView.ContentMode.scaleAspectFit
33
+ } else {
34
+ self.videoView.videoContentMode = UIView.ContentMode.scaleAspectFill
35
+ }
36
+
37
+ if self.local {
38
+ call.updateLocalVideoView(self.videoView)
39
+ }else{
40
+ call.updateRemoteVideoView(self.videoView)
41
+ }
42
+ }
43
+
44
+ func setCallId(callId: String) {
45
+ self.callId = callId
46
+ }
47
+
48
+ func setScaleFit(scaleFit: Bool) {
49
+ self.scaleFit = scaleFit
50
+ }
51
+
52
+ func setLocal(local: Bool) {
53
+ self.local = local
54
+ }
55
+ }
@@ -0,0 +1,6 @@
1
+ #import "React/RCTViewManager.h"
2
+ @interface RCT_EXTERN_MODULE(RNVSendBirdCallsVideoManager, RCTViewManager)
3
+ RCT_EXPORT_VIEW_PROPERTY(callId, NSString)
4
+ RCT_EXPORT_VIEW_PROPERTY(local, BOOL)
5
+ RCT_EXPORT_VIEW_PROPERTY(scaleFit, BOOL)
6
+ @end
@@ -0,0 +1,15 @@
1
+ import SendBirdCalls
2
+
3
+ @objc(RNVSendBirdCallsVideoManager)
4
+ class RNVSendBirdCallsVideoManager: RCTViewManager {
5
+
6
+ override static func requiresMainQueueSetup() -> Bool{
7
+ return true
8
+ }
9
+
10
+ override func view() -> UIView! {
11
+ return RNVSendBirdCallsVideo()
12
+
13
+ }
14
+ }
15
+