@voipsdk/react-native-voipcloud 1.0.0 → 1.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.
- package/CHANGELOG.md +44 -0
- package/README.md +5 -4
- package/android/src/main/java/com/voipcloud/sdk/rn/VoipCloudModule.java +162 -7
- package/android/src/main/java/com/voipcloud/sdk/rn/internal/ManagedAccount.java +49 -5
- package/android/src/main/java/com/voipcloud/sdk/rn/internal/ManagedCall.java +58 -2
- package/android/src/main/java/com/voipcloud/sdk/rn/internal/ManagedCallRegistry.java +4 -0
- package/lib/commonjs/VoipCloud.js +5 -2
- package/lib/commonjs/VoipCloud.js.map +1 -1
- package/lib/module/VoipCloud.js +5 -2
- package/lib/module/VoipCloud.js.map +1 -1
- package/lib/typescript/VoipCloud.d.ts +2 -1
- package/lib/typescript/types.d.ts +4 -0
- package/package.json +3 -2
- package/src/VoipCloud.ts +6 -2
- package/src/types.ts +4 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@voipsdk/react-native-voipcloud` are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.1] - 2026-07-06
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `hangup(callId, mode?)` — optional `mode`: `'busy'`, `'cancel'`, or `'normal'` (default).
|
|
13
|
+
- `reconnectCallAudio(callId)` — re-wire PJSIP audio bridge after Android route changes (speaker/earpiece).
|
|
14
|
+
- `sipCallId` on `incomingCall` and `callState` events — stable SIP Call-ID header across systems.
|
|
15
|
+
- Native sync helpers for background/Telecom UI: `answerCallSync`, `hangupCallSync`, `reconnectCallAudioSync`, `reconnectAllCallsAudioSync`.
|
|
16
|
+
- Automatic 180 Ringing on incoming calls so callers hear ringback before answer.
|
|
17
|
+
- `ManagedCallRegistry.snapshot()` for iterating active calls.
|
|
18
|
+
- VoicePBX hook: `VoipIncomingNotifier.onIncomingCall` dispatched on inbound INVITE (reflection, no hard dependency).
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Register FinalizerDaemon thread with PJSIP to prevent `SIGABRT` ("Calling pjlib from unknown/external thread") when SWIG finalizers run on GC.
|
|
23
|
+
- Disable software AEC (`ecTailLen=0`); rely on Android hardware AEC for VOICE_COMMUNICATION.
|
|
24
|
+
- Audio quality: 48 kHz sound device, quality 10, VAD disabled — reduces crackling from double resampling.
|
|
25
|
+
- NAT survival: contact/via rewrite, UDP keep-alive (20s), shorter REGISTER interval (120s).
|
|
26
|
+
- Auto-connect capture ↔ call ↔ playback when media becomes active (required for two-way audio on Android).
|
|
27
|
+
- Hold/unhold: defer audio reconnect until `onCallMediaState` confirms media status (fixes hold state desync).
|
|
28
|
+
- Unhold sends `PJSUA_CALL_UNHOLD` flag on re-INVITE.
|
|
29
|
+
- Remove call from registry on disconnect instead of only on explicit hangup.
|
|
30
|
+
- Refresh PJSIP playback devices before reconnecting audio after route changes.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- `ManagedAccount` and `ManagedCall` now hold `Endpoint` reference for audio bridging.
|
|
35
|
+
- Incoming `ManagedCall` constructor requires `ManagedCallRegistry` parameter.
|
|
36
|
+
|
|
37
|
+
## [1.0.0] - 2026-06-30
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- Initial public release: PJSIP VoIP SDK for React Native (Android).
|
|
42
|
+
- Core API: `init`, `shutdown`, `register`, `makeCall`, `answer`, `hangup`, hold/unhold, mute, DTMF, transfer.
|
|
43
|
+
- Event subscriptions via `VoipCloud.on(...)`.
|
|
44
|
+
- Ships `voipcloud-release.aar` with `libvoipcloud.so` for `arm64-v8a`, `armeabi-v7a`, `x86_64`.
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @voipsdk/react-native-voipcloud
|
|
2
2
|
|
|
3
3
|
> Public PJSIP VoIP SDK for React Native — Android only (RN 0.70+, Android 12 / API 31+).
|
|
4
|
-
> Package: `@voipsdk/react-native-voipcloud@1.0.
|
|
4
|
+
> Package: `@voipsdk/react-native-voipcloud@1.0.1`
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -127,7 +127,8 @@ export function useVoipEvents() {
|
|
|
127
127
|
| `unregister(accountId)` | Unregister account |
|
|
128
128
|
| `makeCall(accountId, uri)` | Outbound call → `callId` |
|
|
129
129
|
| `answer(callId)` | Answer incoming call |
|
|
130
|
-
| `hangup(callId)` | End one call |
|
|
130
|
+
| `hangup(callId, mode?)` | End one call (`mode`: `'normal'` \| `'busy'` \| `'cancel'`) |
|
|
131
|
+
| `reconnectCallAudio(callId)` | Re-wire audio after speaker/earpiece route change |
|
|
131
132
|
| `hangupAllCalls()` | End all active calls |
|
|
132
133
|
| `hold(callId)` / `unhold(callId)` | Hold / resume |
|
|
133
134
|
| `mute(callId, on)` | Mute / unmute microphone |
|
|
@@ -160,8 +161,8 @@ export function useVoipEvents() {
|
|
|
160
161
|
| Event | Payload highlights |
|
|
161
162
|
|-------|-------------------|
|
|
162
163
|
| `registrationState` | `{ accountId, code, reason }` |
|
|
163
|
-
| `incomingCall` | `{ accountId, callId, from, to }` |
|
|
164
|
-
| `callState` | `{ callId, state, code, reason }` |
|
|
164
|
+
| `incomingCall` | `{ accountId, callId, from, to, sipCallId? }` |
|
|
165
|
+
| `callState` | `{ callId, state, code, reason, sipCallId? }` |
|
|
165
166
|
| `callMediaState` | `{ callId, hasAudio, isHold, isMuted }` |
|
|
166
167
|
| `callHold` / `callUnhold` | `{ callId }` |
|
|
167
168
|
| `callMute` | `{ callId, isMuted }` |
|
|
@@ -19,7 +19,9 @@ import com.voipcloud.sdk.EpConfig;
|
|
|
19
19
|
import com.voipcloud.sdk.TransportConfig;
|
|
20
20
|
import com.voipcloud.sdk.pjsip_status_code;
|
|
21
21
|
import com.voipcloud.sdk.pjsip_transport_type_e;
|
|
22
|
+
import com.voipcloud.sdk.pjsua_call_flag;
|
|
22
23
|
import com.voipcloud.sdk.pjsua_destroy_flag;
|
|
24
|
+
import com.voipcloud.sdk.AudDevManager;
|
|
23
25
|
|
|
24
26
|
import com.voipcloud.sdk.rn.internal.BridgeGuard;
|
|
25
27
|
import com.voipcloud.sdk.rn.internal.Events;
|
|
@@ -53,6 +55,36 @@ public class VoipCloudModule extends ReactContextBaseJavaModule {
|
|
|
53
55
|
@Override
|
|
54
56
|
public String getName() { return "VoipsdkReactNativeVoipcloud"; }
|
|
55
57
|
|
|
58
|
+
/**
|
|
59
|
+
* PJSIP aborts (SIGABRT, "Calling pjlib from unknown/external thread")
|
|
60
|
+
* when pjlib is entered from a thread never passed to
|
|
61
|
+
* pj_thread_register(). SWIG finalizers run on the runtime's
|
|
62
|
+
* FinalizerDaemon thread and call native destructors that touch pjlib
|
|
63
|
+
* (CallOpParam, EpConfig, CallInfo, ... dropped to the GC), so that
|
|
64
|
+
* thread must be registered too. There is no API to run code on
|
|
65
|
+
* FinalizerDaemon directly — enqueue a sacrificial object whose
|
|
66
|
+
* finalizer registers it, and force a finalization pass now, while no
|
|
67
|
+
* other SDK garbage is pending yet.
|
|
68
|
+
*/
|
|
69
|
+
private static void registerFinalizerDaemonThread(final Endpoint ep) {
|
|
70
|
+
new Object() {
|
|
71
|
+
@Override
|
|
72
|
+
protected void finalize() throws Throwable {
|
|
73
|
+
try {
|
|
74
|
+
if (!ep.libIsThreadRegistered()) {
|
|
75
|
+
ep.libRegisterThread("FinalizerDaemon");
|
|
76
|
+
}
|
|
77
|
+
} catch (Throwable ignored) {
|
|
78
|
+
// Endpoint already destroyed or registration raced — the
|
|
79
|
+
// next init() enqueues a fresh registration.
|
|
80
|
+
}
|
|
81
|
+
super.finalize();
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
System.gc();
|
|
85
|
+
System.runFinalization();
|
|
86
|
+
}
|
|
87
|
+
|
|
56
88
|
private void destroyEndpointQuietly() {
|
|
57
89
|
if (endpoint == null) return;
|
|
58
90
|
try {
|
|
@@ -76,6 +108,7 @@ public class VoipCloudModule extends ReactContextBaseJavaModule {
|
|
|
76
108
|
try {
|
|
77
109
|
ep = new Endpoint();
|
|
78
110
|
ep.libCreate();
|
|
111
|
+
registerFinalizerDaemonThread(ep);
|
|
79
112
|
|
|
80
113
|
EpConfig epCfg = new EpConfig();
|
|
81
114
|
if (config.hasKey("userAgent") && !config.isNull("userAgent"))
|
|
@@ -88,6 +121,26 @@ public class VoipCloudModule extends ReactContextBaseJavaModule {
|
|
|
88
121
|
if (config.hasKey("logLevel") && !config.isNull("logLevel"))
|
|
89
122
|
epCfg.getLogConfig().setLevel(config.getInt("logLevel"));
|
|
90
123
|
|
|
124
|
+
// Disable PJSIP's software echo canceller: it is CPU-heavy on
|
|
125
|
+
// mobile and a common cause of crackling/choppy audio. Android
|
|
126
|
+
// applies its hardware AEC to VOICE_COMMUNICATION streams.
|
|
127
|
+
try {
|
|
128
|
+
epCfg.getMedConfig().setEcTailLen(0);
|
|
129
|
+
} catch (Throwable ignored) {}
|
|
130
|
+
|
|
131
|
+
// Audio quality: run the sound device at the Android native rate
|
|
132
|
+
// (48 kHz) so the HAL does not resample a 16 kHz stream a second
|
|
133
|
+
// time (double resampling on the JNI audio backend is the main
|
|
134
|
+
// source of crackling/harsh speaker audio). The conference
|
|
135
|
+
// bridge stays at 16 kHz; PJSIP resamples once with the
|
|
136
|
+
// high-quality (large filter) resampler. VAD is disabled so
|
|
137
|
+
// silence suppression cannot chop word edges.
|
|
138
|
+
try {
|
|
139
|
+
epCfg.getMedConfig().setSndClockRate(48000);
|
|
140
|
+
epCfg.getMedConfig().setQuality(10);
|
|
141
|
+
epCfg.getMedConfig().setNoVad(true);
|
|
142
|
+
} catch (Throwable ignored) {}
|
|
143
|
+
|
|
91
144
|
ep.libInit(epCfg);
|
|
92
145
|
|
|
93
146
|
TransportConfig udp = new TransportConfig();
|
|
@@ -146,10 +199,21 @@ public class VoipCloudModule extends ReactContextBaseJavaModule {
|
|
|
146
199
|
acfg.setIdUri(uri);
|
|
147
200
|
acfg.getRegConfig().setRegistrarUri(registrar);
|
|
148
201
|
|
|
202
|
+
// NAT survival: without keep-alives and contact rewrite the
|
|
203
|
+
// registration "succeeds" but the NAT mapping dies within a
|
|
204
|
+
// minute and inbound INVITEs never reach the device.
|
|
205
|
+
acfg.getNatConfig().setContactRewriteUse(1);
|
|
206
|
+
acfg.getNatConfig().setViaRewriteUse(1);
|
|
207
|
+
acfg.getNatConfig().setUdpKaIntervalSec(20);
|
|
208
|
+
// Short re-REGISTER interval keeps the binding fresh behind NAT.
|
|
209
|
+
acfg.getRegConfig().setTimeoutSec(120);
|
|
210
|
+
acfg.getRegConfig().setFirstRetryIntervalSec(5);
|
|
211
|
+
acfg.getRegConfig().setRetryIntervalSec(30);
|
|
212
|
+
|
|
149
213
|
AuthCredInfo cred = new AuthCredInfo("digest", "*", username, 0, password);
|
|
150
214
|
acfg.getSipConfig().getAuthCreds().add(cred);
|
|
151
215
|
|
|
152
|
-
ManagedAccount acc = new ManagedAccount(ctx, calls);
|
|
216
|
+
ManagedAccount acc = new ManagedAccount(ctx, calls, endpoint);
|
|
153
217
|
acc.create(acfg);
|
|
154
218
|
int id = acc.accountId();
|
|
155
219
|
if (id < 0) {
|
|
@@ -195,7 +259,8 @@ public class VoipCloudModule extends ReactContextBaseJavaModule {
|
|
|
195
259
|
promise.reject("VC_NO_ACCOUNT", "Unknown accountId " + accountId + ". Call register() first.");
|
|
196
260
|
return;
|
|
197
261
|
}
|
|
198
|
-
ManagedCall call = new ManagedCall(ctx, acc);
|
|
262
|
+
ManagedCall call = new ManagedCall(ctx, acc, calls);
|
|
263
|
+
call.bindEndpoint(endpoint);
|
|
199
264
|
call.makeCall(uri.trim(), new CallOpParam(true));
|
|
200
265
|
int callId = call.callId();
|
|
201
266
|
if (callId < 0) {
|
|
@@ -229,21 +294,57 @@ public class VoipCloudModule extends ReactContextBaseJavaModule {
|
|
|
229
294
|
}
|
|
230
295
|
|
|
231
296
|
@ReactMethod
|
|
232
|
-
public void hangup(int callId, Promise promise) {
|
|
297
|
+
public void hangup(int callId, String mode, Promise promise) {
|
|
233
298
|
if (!BridgeGuard.ensureNative(promise)) return;
|
|
234
299
|
try {
|
|
235
300
|
ManagedCall call = calls.get(callId);
|
|
236
301
|
if (call == null) { promise.resolve(null); return; }
|
|
237
|
-
CallOpParam op = new CallOpParam();
|
|
238
|
-
|
|
302
|
+
CallOpParam op = new CallOpParam(true);
|
|
303
|
+
if ("busy".equals(mode)) {
|
|
304
|
+
op.setStatusCode(pjsip_status_code.PJSIP_SC_BUSY_HERE);
|
|
305
|
+
} else if ("cancel".equals(mode)) {
|
|
306
|
+
op.setStatusCode(pjsip_status_code.PJSIP_SC_REQUEST_TERMINATED);
|
|
307
|
+
}
|
|
239
308
|
call.hangup(op);
|
|
240
|
-
calls.remove(callId);
|
|
241
309
|
promise.resolve(null);
|
|
242
310
|
} catch (Throwable t) {
|
|
243
311
|
BridgeGuard.reject(promise, "VC_HANGUP", "Failed to hang up call", t);
|
|
244
312
|
}
|
|
245
313
|
}
|
|
246
314
|
|
|
315
|
+
/** Synchronous answer for native notification / Telecom UI when JS is inactive. */
|
|
316
|
+
public boolean answerCallSync(int callId) {
|
|
317
|
+
if (!inited) return false;
|
|
318
|
+
try {
|
|
319
|
+
ManagedCall call = calls.get(callId);
|
|
320
|
+
if (call == null) return false;
|
|
321
|
+
CallOpParam op = new CallOpParam();
|
|
322
|
+
op.setStatusCode(pjsip_status_code.PJSIP_SC_OK);
|
|
323
|
+
call.answer(op);
|
|
324
|
+
return true;
|
|
325
|
+
} catch (Throwable t) {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Synchronous hangup for native notification / Telecom UI when JS is inactive. */
|
|
331
|
+
public boolean hangupCallSync(int callId, String mode) {
|
|
332
|
+
try {
|
|
333
|
+
ManagedCall call = calls.get(callId);
|
|
334
|
+
if (call == null) return false;
|
|
335
|
+
CallOpParam op = new CallOpParam(true);
|
|
336
|
+
if ("busy".equals(mode)) {
|
|
337
|
+
op.setStatusCode(pjsip_status_code.PJSIP_SC_BUSY_HERE);
|
|
338
|
+
} else if ("cancel".equals(mode)) {
|
|
339
|
+
op.setStatusCode(pjsip_status_code.PJSIP_SC_REQUEST_TERMINATED);
|
|
340
|
+
}
|
|
341
|
+
call.hangup(op);
|
|
342
|
+
return true;
|
|
343
|
+
} catch (Throwable t) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
247
348
|
@ReactMethod
|
|
248
349
|
public void hold(int callId, Promise promise) {
|
|
249
350
|
if (!BridgeGuard.ensureNative(promise)) return;
|
|
@@ -252,6 +353,10 @@ public class VoipCloudModule extends ReactContextBaseJavaModule {
|
|
|
252
353
|
ManagedCall call = requireCall(callId, promise);
|
|
253
354
|
if (call == null) return;
|
|
254
355
|
call.setHold(new CallOpParam(true));
|
|
356
|
+
// Do NOT reconnect audio here: the hold re-INVITE is still in
|
|
357
|
+
// flight, so reconnecting emits a media-state snapshot with the
|
|
358
|
+
// OLD hold flag and desyncs the JS hold state. onCallMediaState
|
|
359
|
+
// rewires the bridge once the media status actually changes.
|
|
255
360
|
WritableMap ev = Arguments.createMap();
|
|
256
361
|
ev.putInt("callId", callId);
|
|
257
362
|
Events.emit(getReactApplicationContext(), Events.CALL_HOLD, ev);
|
|
@@ -268,7 +373,11 @@ public class VoipCloudModule extends ReactContextBaseJavaModule {
|
|
|
268
373
|
try {
|
|
269
374
|
ManagedCall call = requireCall(callId, promise);
|
|
270
375
|
if (call == null) return;
|
|
271
|
-
|
|
376
|
+
CallOpParam prm = new CallOpParam(true);
|
|
377
|
+
prm.getOpt().setFlag(pjsua_call_flag.PJSUA_CALL_UNHOLD);
|
|
378
|
+
call.reinvite(prm);
|
|
379
|
+
// See hold(): audio is rewired by onCallMediaState when the
|
|
380
|
+
// unhold re-INVITE completes and media becomes ACTIVE again.
|
|
272
381
|
WritableMap ev = Arguments.createMap();
|
|
273
382
|
ev.putInt("callId", callId);
|
|
274
383
|
Events.emit(getReactApplicationContext(), Events.CALL_UNHOLD, ev);
|
|
@@ -278,6 +387,52 @@ public class VoipCloudModule extends ReactContextBaseJavaModule {
|
|
|
278
387
|
}
|
|
279
388
|
}
|
|
280
389
|
|
|
390
|
+
private void refreshPjsipPlaybackDevices() {
|
|
391
|
+
if (endpoint == null) return;
|
|
392
|
+
try {
|
|
393
|
+
AudDevManager adm = endpoint.audDevManager();
|
|
394
|
+
adm.refreshDevs();
|
|
395
|
+
} catch (Throwable ignored) {}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** Reconnect PJSIP audio bridge after Android route changes (speaker/earpiece). */
|
|
399
|
+
public boolean reconnectCallAudioSync(int callId) {
|
|
400
|
+
if (!inited) return false;
|
|
401
|
+
ManagedCall call = calls.get(callId);
|
|
402
|
+
if (call == null) return false;
|
|
403
|
+
try {
|
|
404
|
+
refreshPjsipPlaybackDevices();
|
|
405
|
+
call.reconnectAudioMedia();
|
|
406
|
+
return true;
|
|
407
|
+
} catch (Throwable t) {
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
public int reconnectAllCallsAudioSync() {
|
|
413
|
+
if (!inited) return 0;
|
|
414
|
+
refreshPjsipPlaybackDevices();
|
|
415
|
+
int count = 0;
|
|
416
|
+
for (ManagedCall call : calls.snapshot()) {
|
|
417
|
+
try {
|
|
418
|
+
call.reconnectAudioMedia();
|
|
419
|
+
count++;
|
|
420
|
+
} catch (Throwable ignored) {}
|
|
421
|
+
}
|
|
422
|
+
return count;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
@ReactMethod
|
|
426
|
+
public void reconnectCallAudio(int callId, Promise promise) {
|
|
427
|
+
if (!BridgeGuard.ensureNative(promise)) return;
|
|
428
|
+
if (!BridgeGuard.ensureInited(inited, promise)) return;
|
|
429
|
+
if (reconnectCallAudioSync(callId)) {
|
|
430
|
+
promise.resolve(null);
|
|
431
|
+
} else {
|
|
432
|
+
promise.reject("VC_AUDIO", "Failed to reconnect call audio");
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
281
436
|
@ReactMethod
|
|
282
437
|
public void mute(int callId, boolean on, Promise promise) {
|
|
283
438
|
if (!BridgeGuard.ensureNative(promise)) return;
|
|
@@ -7,8 +7,10 @@ import com.facebook.react.bridge.WritableMap;
|
|
|
7
7
|
import com.voipcloud.sdk.Account;
|
|
8
8
|
import com.voipcloud.sdk.AccountInfo;
|
|
9
9
|
import com.voipcloud.sdk.CallOpParam;
|
|
10
|
+
import com.voipcloud.sdk.Endpoint;
|
|
10
11
|
import com.voipcloud.sdk.OnIncomingCallParam;
|
|
11
12
|
import com.voipcloud.sdk.OnRegStateParam;
|
|
13
|
+
import com.voipcloud.sdk.pjsip_status_code;
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* Account subclass forwarding PJSUA2 callbacks to JS as RN events.
|
|
@@ -17,10 +19,12 @@ import com.voipcloud.sdk.OnRegStateParam;
|
|
|
17
19
|
public final class ManagedAccount extends Account {
|
|
18
20
|
private final ReactContext ctx;
|
|
19
21
|
private final ManagedCallRegistry callRegistry;
|
|
22
|
+
private final Endpoint endpoint;
|
|
20
23
|
|
|
21
|
-
public ManagedAccount(ReactContext ctx, ManagedCallRegistry callRegistry) {
|
|
24
|
+
public ManagedAccount(ReactContext ctx, ManagedCallRegistry callRegistry, Endpoint endpoint) {
|
|
22
25
|
this.ctx = ctx;
|
|
23
26
|
this.callRegistry = callRegistry;
|
|
27
|
+
this.endpoint = endpoint;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
public int accountId() {
|
|
@@ -41,23 +45,63 @@ public final class ManagedAccount extends Account {
|
|
|
41
45
|
// Allocate a ManagedCall so we can fire callState events for
|
|
42
46
|
// this inbound leg. The consumer must call answer(callId) or
|
|
43
47
|
// hangup(callId) to proceed.
|
|
44
|
-
ManagedCall call = new ManagedCall(ctx, this, prm.getCallId());
|
|
48
|
+
ManagedCall call = new ManagedCall(ctx, this, prm.getCallId(), callRegistry);
|
|
49
|
+
call.bindEndpoint(endpoint);
|
|
45
50
|
callRegistry.put(call);
|
|
46
51
|
|
|
52
|
+
// Send 180 Ringing immediately: without a provisional response the
|
|
53
|
+
// caller hears dead silence and many PBX/trunk setups drop the call
|
|
54
|
+
// before the callee can answer.
|
|
55
|
+
try {
|
|
56
|
+
CallOpParam ringing = new CallOpParam();
|
|
57
|
+
ringing.setStatusCode(pjsip_status_code.PJSIP_SC_RINGING);
|
|
58
|
+
call.answer(ringing);
|
|
59
|
+
} catch (Throwable ignored) {}
|
|
60
|
+
|
|
61
|
+
String from = "";
|
|
62
|
+
String to = "";
|
|
47
63
|
try {
|
|
48
64
|
AccountInfo accInfo = getInfo();
|
|
65
|
+
com.voipcloud.sdk.CallInfo callInfo = call.getInfo();
|
|
66
|
+
from = callInfo.getRemoteUri();
|
|
67
|
+
to = accInfo.getUri();
|
|
49
68
|
WritableMap payload = Arguments.createMap();
|
|
50
69
|
payload.putInt("accountId", accountId());
|
|
51
70
|
payload.putInt("callId", call.callId());
|
|
52
|
-
payload.putString("from",
|
|
53
|
-
payload.putString("to",
|
|
71
|
+
payload.putString("from", from);
|
|
72
|
+
payload.putString("to", to);
|
|
73
|
+
// SIP Call-ID header — stable across systems (PBX CDR, CRM sync),
|
|
74
|
+
// unlike the numeric PJSUA call slot which is recycled.
|
|
75
|
+
payload.putString("sipCallId", callInfo.getCallIdString());
|
|
54
76
|
Events.emit(ctx, Events.INCOMING_CALL, payload);
|
|
55
77
|
} catch (Exception ignored) {
|
|
56
|
-
// If we cannot pull info, still notify with what we have.
|
|
57
78
|
WritableMap payload = Arguments.createMap();
|
|
58
79
|
payload.putInt("accountId", accountId());
|
|
59
80
|
payload.putInt("callId", call.callId());
|
|
60
81
|
Events.emit(ctx, Events.INCOMING_CALL, payload);
|
|
61
82
|
}
|
|
83
|
+
dispatchIncomingToApp(ctx, accountId(), call.callId(), from, to);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Notify VoicePBX native layer before JS (background incoming calls). */
|
|
87
|
+
private static void dispatchIncomingToApp(
|
|
88
|
+
ReactContext ctx, int accountId, int callId, String from, String to) {
|
|
89
|
+
try {
|
|
90
|
+
Class<?> cls = Class.forName("com.voicepbx.voip.VoipIncomingNotifier");
|
|
91
|
+
cls.getMethod(
|
|
92
|
+
"onIncomingCall",
|
|
93
|
+
android.content.Context.class,
|
|
94
|
+
int.class,
|
|
95
|
+
int.class,
|
|
96
|
+
String.class,
|
|
97
|
+
String.class)
|
|
98
|
+
.invoke(
|
|
99
|
+
null,
|
|
100
|
+
ctx.getApplicationContext(),
|
|
101
|
+
accountId,
|
|
102
|
+
callId,
|
|
103
|
+
from != null ? from : "",
|
|
104
|
+
to != null ? to : "");
|
|
105
|
+
} catch (Throwable ignored) {}
|
|
62
106
|
}
|
|
63
107
|
}
|
|
@@ -22,23 +22,31 @@ import com.voipcloud.sdk.pjsua_call_media_status;
|
|
|
22
22
|
/** Call subclass forwarding state callbacks to JS. */
|
|
23
23
|
public final class ManagedCall extends Call {
|
|
24
24
|
private final ReactContext ctx;
|
|
25
|
+
private final ManagedCallRegistry registry;
|
|
25
26
|
/** Local snapshot of the PJSUA call id — Call.getId() can throw post-disconnect. */
|
|
26
27
|
private final int cachedId;
|
|
27
28
|
/** Mute is tracked at the bridge level — PJSIP does not own this state. */
|
|
28
29
|
private volatile boolean isMuted = false;
|
|
30
|
+
private Endpoint endpoint;
|
|
29
31
|
|
|
30
|
-
public ManagedCall(ReactContext ctx, Account acc) {
|
|
32
|
+
public ManagedCall(ReactContext ctx, Account acc, ManagedCallRegistry registry) {
|
|
31
33
|
super(acc);
|
|
32
34
|
this.ctx = ctx;
|
|
35
|
+
this.registry = registry;
|
|
33
36
|
this.cachedId = -1;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
public ManagedCall(ReactContext ctx, Account acc, int callId) {
|
|
39
|
+
public ManagedCall(ReactContext ctx, Account acc, int callId, ManagedCallRegistry registry) {
|
|
37
40
|
super(acc, callId);
|
|
38
41
|
this.ctx = ctx;
|
|
42
|
+
this.registry = registry;
|
|
39
43
|
this.cachedId = callId;
|
|
40
44
|
}
|
|
41
45
|
|
|
46
|
+
public void bindEndpoint(Endpoint ep) {
|
|
47
|
+
this.endpoint = ep;
|
|
48
|
+
}
|
|
49
|
+
|
|
42
50
|
public int callId() {
|
|
43
51
|
try { return getId(); } catch (Exception e) { return cachedId; }
|
|
44
52
|
}
|
|
@@ -54,6 +62,7 @@ public final class ManagedCall extends Call {
|
|
|
54
62
|
* tracked so unhold-then-emit reflects the user's intent.
|
|
55
63
|
*/
|
|
56
64
|
public void setMuted(Endpoint endpoint, boolean muted) throws Exception {
|
|
65
|
+
this.endpoint = endpoint;
|
|
57
66
|
AudDevManager adm = endpoint.audDevManager();
|
|
58
67
|
AudioMedia mic = adm.getCaptureDevMedia();
|
|
59
68
|
CallInfo info = getInfo();
|
|
@@ -74,6 +83,43 @@ public final class ManagedCall extends Call {
|
|
|
74
83
|
emitMediaState();
|
|
75
84
|
}
|
|
76
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Re-wire capture ↔ call ↔ playback (e.g. after hold/unhold or speaker change).
|
|
88
|
+
*/
|
|
89
|
+
public void reconnectAudioMedia() {
|
|
90
|
+
connectAudioMedia();
|
|
91
|
+
emitMediaState();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Wire capture ↔ call ↔ playback whenever media becomes active.
|
|
96
|
+
* Required for two-way audio — PJSIP does not auto-bridge on Android.
|
|
97
|
+
*/
|
|
98
|
+
private void connectAudioMedia() {
|
|
99
|
+
if (endpoint == null) return;
|
|
100
|
+
try {
|
|
101
|
+
AudDevManager adm = endpoint.audDevManager();
|
|
102
|
+
AudioMedia mic = adm.getCaptureDevMedia();
|
|
103
|
+
AudioMedia speaker = adm.getPlaybackDevMedia();
|
|
104
|
+
CallInfo info = getInfo();
|
|
105
|
+
for (int i = 0; i < info.getMedia().size(); i++) {
|
|
106
|
+
CallMediaInfo mi = info.getMedia().get(i);
|
|
107
|
+
if (mi.getType() != pjmedia_type.PJMEDIA_TYPE_AUDIO) continue;
|
|
108
|
+
AudioMedia leg = getAudioMedia(i);
|
|
109
|
+
int status = mi.getStatus();
|
|
110
|
+
if (status == pjsua_call_media_status.PJSUA_CALL_MEDIA_ACTIVE) {
|
|
111
|
+
if (!isMuted) {
|
|
112
|
+
mic.startTransmit(leg);
|
|
113
|
+
}
|
|
114
|
+
leg.startTransmit(speaker);
|
|
115
|
+
} else {
|
|
116
|
+
try { mic.stopTransmit(leg); } catch (Exception ignored) {}
|
|
117
|
+
try { leg.stopTransmit(speaker); } catch (Exception ignored) {}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} catch (Exception ignored) {}
|
|
121
|
+
}
|
|
122
|
+
|
|
77
123
|
@Override
|
|
78
124
|
public void onCallState(OnCallStateParam prm) {
|
|
79
125
|
CallInfo info;
|
|
@@ -86,6 +132,7 @@ public final class ManagedCall extends Call {
|
|
|
86
132
|
p.putInt("code", 0);
|
|
87
133
|
p.putString("reason", "");
|
|
88
134
|
Events.emit(ctx, Events.CALL_STATE, p);
|
|
135
|
+
if (cachedId >= 0) registry.remove(cachedId);
|
|
89
136
|
return;
|
|
90
137
|
}
|
|
91
138
|
WritableMap p = Arguments.createMap();
|
|
@@ -93,11 +140,20 @@ public final class ManagedCall extends Call {
|
|
|
93
140
|
p.putString("state", invStateName(info.getState()));
|
|
94
141
|
p.putInt("code", info.getLastStatusCode());
|
|
95
142
|
p.putString("reason", info.getLastReason());
|
|
143
|
+
// SIP Call-ID header — lets JS attach the dialog id to outgoing
|
|
144
|
+
// calls too (makeCall only returns the recycled PJSUA slot).
|
|
145
|
+
try { p.putString("sipCallId", info.getCallIdString()); } catch (Exception ignored) {}
|
|
96
146
|
Events.emit(ctx, Events.CALL_STATE, p);
|
|
147
|
+
int state = info.getState();
|
|
148
|
+
if (state == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED
|
|
149
|
+
|| state == pjsip_inv_state.PJSIP_INV_STATE_NULL) {
|
|
150
|
+
registry.remove(callId());
|
|
151
|
+
}
|
|
97
152
|
}
|
|
98
153
|
|
|
99
154
|
@Override
|
|
100
155
|
public void onCallMediaState(OnCallMediaStateParam prm) {
|
|
156
|
+
connectAudioMedia();
|
|
101
157
|
emitMediaState();
|
|
102
158
|
}
|
|
103
159
|
|
|
@@ -45,8 +45,8 @@ const VoipCloud = exports.VoipCloud = {
|
|
|
45
45
|
answer(callId) {
|
|
46
46
|
return callNative(() => _nativeModule.NativeVoipCloud.answer(callId));
|
|
47
47
|
},
|
|
48
|
-
hangup(callId) {
|
|
49
|
-
return callNative(() => _nativeModule.NativeVoipCloud.hangup(callId));
|
|
48
|
+
hangup(callId, mode = 'normal') {
|
|
49
|
+
return callNative(() => _nativeModule.NativeVoipCloud.hangup(callId, mode));
|
|
50
50
|
},
|
|
51
51
|
hold(callId) {
|
|
52
52
|
return callNative(() => _nativeModule.NativeVoipCloud.hold(callId));
|
|
@@ -66,6 +66,9 @@ const VoipCloud = exports.VoipCloud = {
|
|
|
66
66
|
hangupAllCalls() {
|
|
67
67
|
return callNative(() => _nativeModule.NativeVoipCloud.hangupAllCalls());
|
|
68
68
|
},
|
|
69
|
+
reconnectCallAudio(callId) {
|
|
70
|
+
return callNative(() => _nativeModule.NativeVoipCloud.reconnectCallAudio(callId));
|
|
71
|
+
},
|
|
69
72
|
on: _events.on
|
|
70
73
|
};
|
|
71
74
|
//# sourceMappingURL=VoipCloud.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_nativeModule","require","_events","rejectNotLinked","Promise","reject","Object","assign","Error","code","callNative","fn","NativeVoipCloud","VoipCloud","exports","init","cfg","shutdown","register","account","unregister","accountId","makeCall","destinationUri","answer","callId","hangup","hold","unhold","mute","on","sendDtmf","digits","transferCall","hangupAllCalls"],"sourceRoot":"../../src","sources":["VoipCloud.ts"],"mappings":";;;;;;AACA,IAAAA,aAAA,GAAAC,OAAA;AACA,IAAAC,OAAA,GAAAD,OAAA;AAEA;AACA,SAASE,eAAeA,CAAA,EAAmB;EACvC,OAAOC,OAAO,CAACC,MAAM,CACjBC,MAAM,CAACC,MAAM,CACT,IAAIC,KAAK,CACL,+DAA+D,GAC/D,+CAA+C,GAC/C,gFACJ,CAAC,EACD;IAAEC,IAAI,EAAE;EAAgB,CAC5B,CACJ,CAAC;AACL;AAEA,SAASC,UAAUA,CAAIC,EAAoB,EAAc;EACrD,IAAI,CAACC,6BAAe,EAAE,OAAOT,eAAe,CAAC,CAAC;EAC9C,OAAOQ,EAAE,CAAC,CAAC;AACf;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAME,SAAS,GAAAC,OAAA,CAAAD,SAAA,GAAG;EACrBE,IAAIA,CAACC,GAAe,GAAG,CAAC,CAAC,EAAiB;IACtC,OAAON,UAAU,CAAC,MAAME,6BAAe,CAACG,IAAI,CAACC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;EAC5D,CAAC;EAEDC,QAAQA,CAAA,EAAkB;IACtB,OAAOP,UAAU,CAAC,MAAME,6BAAe,CAACK,QAAQ,CAAC,CAAC,CAAC;EACvD,CAAC;EAEDC,QAAQA,CAACC,OAAsB,EAAmB;IAC9C,OAAOT,UAAU,CAAC,MAAME,6BAAe,CAACM,QAAQ,CAACC,OAAO,CAAC,CAAC;EAC9D,CAAC;EAEDC,UAAUA,CAACC,SAAiB,EAAiB;IACzC,OAAOX,UAAU,CAAC,MAAME,6BAAe,CAACQ,UAAU,CAACC,SAAS,CAAC,CAAC;EAClE,CAAC;EAEDC,QAAQA,CAACD,SAAiB,EAAEE,cAAsB,EAAmB;IACjE,OAAOb,UAAU,CAAC,MAAME,6BAAe,CAACU,QAAQ,CAACD,SAAS,EAAEE,cAAc,CAAC,CAAC;EAChF,CAAC;EAEDC,MAAMA,CAACC,MAAc,EAAiB;IAClC,OAAOf,UAAU,CAAC,MAAME,6BAAe,CAACY,MAAM,CAACC,MAAM,CAAC,CAAC;EAC3D,CAAC;EAEDC,MAAMA,CAACD,MAAc,EAAiB;
|
|
1
|
+
{"version":3,"names":["_nativeModule","require","_events","rejectNotLinked","Promise","reject","Object","assign","Error","code","callNative","fn","NativeVoipCloud","VoipCloud","exports","init","cfg","shutdown","register","account","unregister","accountId","makeCall","destinationUri","answer","callId","hangup","mode","hold","unhold","mute","on","sendDtmf","digits","transferCall","hangupAllCalls","reconnectCallAudio"],"sourceRoot":"../../src","sources":["VoipCloud.ts"],"mappings":";;;;;;AACA,IAAAA,aAAA,GAAAC,OAAA;AACA,IAAAC,OAAA,GAAAD,OAAA;AAEA;AACA,SAASE,eAAeA,CAAA,EAAmB;EACvC,OAAOC,OAAO,CAACC,MAAM,CACjBC,MAAM,CAACC,MAAM,CACT,IAAIC,KAAK,CACL,+DAA+D,GAC/D,+CAA+C,GAC/C,gFACJ,CAAC,EACD;IAAEC,IAAI,EAAE;EAAgB,CAC5B,CACJ,CAAC;AACL;AAEA,SAASC,UAAUA,CAAIC,EAAoB,EAAc;EACrD,IAAI,CAACC,6BAAe,EAAE,OAAOT,eAAe,CAAC,CAAC;EAC9C,OAAOQ,EAAE,CAAC,CAAC;AACf;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAME,SAAS,GAAAC,OAAA,CAAAD,SAAA,GAAG;EACrBE,IAAIA,CAACC,GAAe,GAAG,CAAC,CAAC,EAAiB;IACtC,OAAON,UAAU,CAAC,MAAME,6BAAe,CAACG,IAAI,CAACC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;EAC5D,CAAC;EAEDC,QAAQA,CAAA,EAAkB;IACtB,OAAOP,UAAU,CAAC,MAAME,6BAAe,CAACK,QAAQ,CAAC,CAAC,CAAC;EACvD,CAAC;EAEDC,QAAQA,CAACC,OAAsB,EAAmB;IAC9C,OAAOT,UAAU,CAAC,MAAME,6BAAe,CAACM,QAAQ,CAACC,OAAO,CAAC,CAAC;EAC9D,CAAC;EAEDC,UAAUA,CAACC,SAAiB,EAAiB;IACzC,OAAOX,UAAU,CAAC,MAAME,6BAAe,CAACQ,UAAU,CAACC,SAAS,CAAC,CAAC;EAClE,CAAC;EAEDC,QAAQA,CAACD,SAAiB,EAAEE,cAAsB,EAAmB;IACjE,OAAOb,UAAU,CAAC,MAAME,6BAAe,CAACU,QAAQ,CAACD,SAAS,EAAEE,cAAc,CAAC,CAAC;EAChF,CAAC;EAEDC,MAAMA,CAACC,MAAc,EAAiB;IAClC,OAAOf,UAAU,CAAC,MAAME,6BAAe,CAACY,MAAM,CAACC,MAAM,CAAC,CAAC;EAC3D,CAAC;EAEDC,MAAMA,CAACD,MAAc,EAAEE,IAAkC,GAAG,QAAQ,EAAiB;IACjF,OAAOjB,UAAU,CAAC,MAAME,6BAAe,CAACc,MAAM,CAACD,MAAM,EAAEE,IAAI,CAAC,CAAC;EACjE,CAAC;EAEDC,IAAIA,CAACH,MAAc,EAAiB;IAChC,OAAOf,UAAU,CAAC,MAAME,6BAAe,CAACgB,IAAI,CAACH,MAAM,CAAC,CAAC;EACzD,CAAC;EAEDI,MAAMA,CAACJ,MAAc,EAAiB;IAClC,OAAOf,UAAU,CAAC,MAAME,6BAAe,CAACiB,MAAM,CAACJ,MAAM,CAAC,CAAC;EAC3D,CAAC;EAEDK,IAAIA,CAACL,MAAc,EAAEM,EAAW,EAAiB;IAC7C,OAAOrB,UAAU,CAAC,MAAME,6BAAe,CAACkB,IAAI,CAACL,MAAM,EAAEM,EAAE,CAAC,CAAC;EAC7D,CAAC;EAEDC,QAAQA,CAACP,MAAc,EAAEQ,MAAc,EAAiB;IACpD,OAAOvB,UAAU,CAAC,MAAME,6BAAe,CAACoB,QAAQ,CAACP,MAAM,EAAEQ,MAAM,CAAC,CAAC;EACrE,CAAC;EAEDC,YAAYA,CAACT,MAAc,EAAEF,cAAsB,EAAiB;IAChE,OAAOb,UAAU,CAAC,MAAME,6BAAe,CAACsB,YAAY,CAACT,MAAM,EAAEF,cAAc,CAAC,CAAC;EACjF,CAAC;EAEDY,cAAcA,CAAA,EAAkB;IAC5B,OAAOzB,UAAU,CAAC,MAAME,6BAAe,CAACuB,cAAc,CAAC,CAAC,CAAC;EAC7D,CAAC;EAEDC,kBAAkBA,CAACX,MAAc,EAAiB;IAC9C,OAAOf,UAAU,CAAC,MAAME,6BAAe,CAACwB,kBAAkB,CAACX,MAAM,CAAC,CAAC;EACvE,CAAC;EAEDM,EAAE,EAAFA;AACJ,CAAC","ignoreList":[]}
|
package/lib/module/VoipCloud.js
CHANGED
|
@@ -40,8 +40,8 @@ export const VoipCloud = {
|
|
|
40
40
|
answer(callId) {
|
|
41
41
|
return callNative(() => NativeVoipCloud.answer(callId));
|
|
42
42
|
},
|
|
43
|
-
hangup(callId) {
|
|
44
|
-
return callNative(() => NativeVoipCloud.hangup(callId));
|
|
43
|
+
hangup(callId, mode = 'normal') {
|
|
44
|
+
return callNative(() => NativeVoipCloud.hangup(callId, mode));
|
|
45
45
|
},
|
|
46
46
|
hold(callId) {
|
|
47
47
|
return callNative(() => NativeVoipCloud.hold(callId));
|
|
@@ -61,6 +61,9 @@ export const VoipCloud = {
|
|
|
61
61
|
hangupAllCalls() {
|
|
62
62
|
return callNative(() => NativeVoipCloud.hangupAllCalls());
|
|
63
63
|
},
|
|
64
|
+
reconnectCallAudio(callId) {
|
|
65
|
+
return callNative(() => NativeVoipCloud.reconnectCallAudio(callId));
|
|
66
|
+
},
|
|
64
67
|
on
|
|
65
68
|
};
|
|
66
69
|
//# sourceMappingURL=VoipCloud.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NativeVoipCloud","on","rejectNotLinked","Promise","reject","Object","assign","Error","code","callNative","fn","VoipCloud","init","cfg","shutdown","register","account","unregister","accountId","makeCall","destinationUri","answer","callId","hangup","hold","unhold","mute","sendDtmf","digits","transferCall","hangupAllCalls"],"sourceRoot":"../../src","sources":["VoipCloud.ts"],"mappings":"AACA,SAASA,eAAe,QAAQ,gBAAgB;AAChD,SAASC,EAAE,QAAQ,UAAU;;AAE7B;AACA,SAASC,eAAeA,CAAA,EAAmB;EACvC,OAAOC,OAAO,CAACC,MAAM,CACjBC,MAAM,CAACC,MAAM,CACT,IAAIC,KAAK,CACL,+DAA+D,GAC/D,+CAA+C,GAC/C,gFACJ,CAAC,EACD;IAAEC,IAAI,EAAE;EAAgB,CAC5B,CACJ,CAAC;AACL;AAEA,SAASC,UAAUA,CAAIC,EAAoB,EAAc;EACrD,IAAI,CAACV,eAAe,EAAE,OAAOE,eAAe,CAAC,CAAC;EAC9C,OAAOQ,EAAE,CAAC,CAAC;AACf;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,SAAS,GAAG;EACrBC,IAAIA,CAACC,GAAe,GAAG,CAAC,CAAC,EAAiB;IACtC,OAAOJ,UAAU,CAAC,MAAMT,eAAe,CAACY,IAAI,CAACC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;EAC5D,CAAC;EAEDC,QAAQA,CAAA,EAAkB;IACtB,OAAOL,UAAU,CAAC,MAAMT,eAAe,CAACc,QAAQ,CAAC,CAAC,CAAC;EACvD,CAAC;EAEDC,QAAQA,CAACC,OAAsB,EAAmB;IAC9C,OAAOP,UAAU,CAAC,MAAMT,eAAe,CAACe,QAAQ,CAACC,OAAO,CAAC,CAAC;EAC9D,CAAC;EAEDC,UAAUA,CAACC,SAAiB,EAAiB;IACzC,OAAOT,UAAU,CAAC,MAAMT,eAAe,CAACiB,UAAU,CAACC,SAAS,CAAC,CAAC;EAClE,CAAC;EAEDC,QAAQA,CAACD,SAAiB,EAAEE,cAAsB,EAAmB;IACjE,OAAOX,UAAU,CAAC,MAAMT,eAAe,CAACmB,QAAQ,CAACD,SAAS,EAAEE,cAAc,CAAC,CAAC;EAChF,CAAC;EAEDC,MAAMA,CAACC,MAAc,EAAiB;IAClC,OAAOb,UAAU,CAAC,MAAMT,eAAe,CAACqB,MAAM,CAACC,MAAM,CAAC,CAAC;EAC3D,CAAC;EAEDC,MAAMA,CAACD,MAAc,EAAiB;
|
|
1
|
+
{"version":3,"names":["NativeVoipCloud","on","rejectNotLinked","Promise","reject","Object","assign","Error","code","callNative","fn","VoipCloud","init","cfg","shutdown","register","account","unregister","accountId","makeCall","destinationUri","answer","callId","hangup","mode","hold","unhold","mute","sendDtmf","digits","transferCall","hangupAllCalls","reconnectCallAudio"],"sourceRoot":"../../src","sources":["VoipCloud.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -16,12 +16,13 @@ export declare const VoipCloud: {
|
|
|
16
16
|
unregister(accountId: number): Promise<void>;
|
|
17
17
|
makeCall(accountId: number, destinationUri: string): Promise<number>;
|
|
18
18
|
answer(callId: number): Promise<void>;
|
|
19
|
-
hangup(callId: number): Promise<void>;
|
|
19
|
+
hangup(callId: number, mode?: "busy" | "cancel" | "normal"): Promise<void>;
|
|
20
20
|
hold(callId: number): Promise<void>;
|
|
21
21
|
unhold(callId: number): Promise<void>;
|
|
22
22
|
mute(callId: number, on: boolean): Promise<void>;
|
|
23
23
|
sendDtmf(callId: number, digits: string): Promise<void>;
|
|
24
24
|
transferCall(callId: number, destinationUri: string): Promise<void>;
|
|
25
25
|
hangupAllCalls(): Promise<void>;
|
|
26
|
+
reconnectCallAudio(callId: number): Promise<void>;
|
|
26
27
|
on: typeof on;
|
|
27
28
|
};
|
|
@@ -46,6 +46,8 @@ export interface IncomingCallEvent {
|
|
|
46
46
|
from: string;
|
|
47
47
|
/** Local AOR. */
|
|
48
48
|
to: string;
|
|
49
|
+
/** SIP Call-ID header of the dialog (stable across systems). */
|
|
50
|
+
sipCallId?: string;
|
|
49
51
|
}
|
|
50
52
|
export type CallState = 'NULL' | 'CALLING' | 'INCOMING' | 'EARLY' | 'CONNECTING' | 'CONFIRMED' | 'DISCONNECTED';
|
|
51
53
|
export interface CallStateEvent {
|
|
@@ -53,6 +55,8 @@ export interface CallStateEvent {
|
|
|
53
55
|
state: CallState;
|
|
54
56
|
code: number;
|
|
55
57
|
reason: string;
|
|
58
|
+
/** SIP Call-ID header of the dialog (stable across systems). */
|
|
59
|
+
sipCallId?: string;
|
|
56
60
|
}
|
|
57
61
|
export interface CallMediaStateEvent {
|
|
58
62
|
callId: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voipsdk/react-native-voipcloud",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Public React Native VoIP SDK backed by PJSIP (VoipCloud).",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"ios",
|
|
15
15
|
"react-native-voipcloud.podspec",
|
|
16
16
|
"README.md",
|
|
17
|
+
"CHANGELOG.md",
|
|
17
18
|
"!**/__tests__",
|
|
18
19
|
"!**/.*"
|
|
19
20
|
],
|
|
@@ -42,7 +43,7 @@
|
|
|
42
43
|
"license": "UNLICENSED",
|
|
43
44
|
"repository": {
|
|
44
45
|
"type": "git",
|
|
45
|
-
"url": "https://github.com/hoangdx/react-native-voipcloud.git"
|
|
46
|
+
"url": "git+https://github.com/hoangdx/react-native-voipcloud.git"
|
|
46
47
|
},
|
|
47
48
|
"bugs": {
|
|
48
49
|
"url": "https://github.com/hoangdx/react-native-voipcloud/issues"
|
package/src/VoipCloud.ts
CHANGED
|
@@ -55,8 +55,8 @@ export const VoipCloud = {
|
|
|
55
55
|
return callNative(() => NativeVoipCloud.answer(callId));
|
|
56
56
|
},
|
|
57
57
|
|
|
58
|
-
hangup(callId: number): Promise<void> {
|
|
59
|
-
return callNative(() => NativeVoipCloud.hangup(callId));
|
|
58
|
+
hangup(callId: number, mode: 'busy' | 'cancel' | 'normal' = 'normal'): Promise<void> {
|
|
59
|
+
return callNative(() => NativeVoipCloud.hangup(callId, mode));
|
|
60
60
|
},
|
|
61
61
|
|
|
62
62
|
hold(callId: number): Promise<void> {
|
|
@@ -83,5 +83,9 @@ export const VoipCloud = {
|
|
|
83
83
|
return callNative(() => NativeVoipCloud.hangupAllCalls());
|
|
84
84
|
},
|
|
85
85
|
|
|
86
|
+
reconnectCallAudio(callId: number): Promise<void> {
|
|
87
|
+
return callNative(() => NativeVoipCloud.reconnectCallAudio(callId));
|
|
88
|
+
},
|
|
89
|
+
|
|
86
90
|
on,
|
|
87
91
|
};
|
package/src/types.ts
CHANGED
|
@@ -51,6 +51,8 @@ export interface IncomingCallEvent {
|
|
|
51
51
|
from: string;
|
|
52
52
|
/** Local AOR. */
|
|
53
53
|
to: string;
|
|
54
|
+
/** SIP Call-ID header of the dialog (stable across systems). */
|
|
55
|
+
sipCallId?: string;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
export type CallState =
|
|
@@ -67,6 +69,8 @@ export interface CallStateEvent {
|
|
|
67
69
|
state: CallState;
|
|
68
70
|
code: number;
|
|
69
71
|
reason: string;
|
|
72
|
+
/** SIP Call-ID header of the dialog (stable across systems). */
|
|
73
|
+
sipCallId?: string;
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
export interface CallMediaStateEvent {
|