@voipsdk/react-native-voipcloud 0.2.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 (43) hide show
  1. package/README.md +172 -0
  2. package/android/build.gradle +61 -0
  3. package/android/src/main/AndroidManifest.xml +2 -0
  4. package/android/src/main/java/com/voipcloud/sdk/rn/VoipCloudModule.java +428 -0
  5. package/android/src/main/java/com/voipcloud/sdk/rn/VoipCloudPackage.java +21 -0
  6. package/android/src/main/java/com/voipcloud/sdk/rn/internal/Events.java +38 -0
  7. package/android/src/main/java/com/voipcloud/sdk/rn/internal/ManagedAccount.java +63 -0
  8. package/android/src/main/java/com/voipcloud/sdk/rn/internal/ManagedCall.java +179 -0
  9. package/android/src/main/java/com/voipcloud/sdk/rn/internal/ManagedCallRegistry.java +26 -0
  10. package/android/src/main/libs/voipcloud-release.aar +0 -0
  11. package/ios/VoipCloud.mm +40 -0
  12. package/lib/commonjs/License.js +51 -0
  13. package/lib/commonjs/License.js.map +1 -0
  14. package/lib/commonjs/VoipCloud.js +89 -0
  15. package/lib/commonjs/VoipCloud.js.map +1 -0
  16. package/lib/commonjs/events.js +29 -0
  17. package/lib/commonjs/events.js.map +1 -0
  18. package/lib/commonjs/index.js +27 -0
  19. package/lib/commonjs/index.js.map +1 -0
  20. package/lib/commonjs/types.js +2 -0
  21. package/lib/commonjs/types.js.map +1 -0
  22. package/lib/module/License.js +45 -0
  23. package/lib/module/License.js.map +1 -0
  24. package/lib/module/VoipCloud.js +83 -0
  25. package/lib/module/VoipCloud.js.map +1 -0
  26. package/lib/module/events.js +23 -0
  27. package/lib/module/events.js.map +1 -0
  28. package/lib/module/index.js +4 -0
  29. package/lib/module/index.js.map +1 -0
  30. package/lib/module/types.js +2 -0
  31. package/lib/module/types.js.map +1 -0
  32. package/lib/typescript/License.d.ts +31 -0
  33. package/lib/typescript/VoipCloud.d.ts +52 -0
  34. package/lib/typescript/events.d.ts +6 -0
  35. package/lib/typescript/index.d.ts +4 -0
  36. package/lib/typescript/types.d.ts +136 -0
  37. package/package.json +76 -0
  38. package/react-native-voipcloud.podspec +16 -0
  39. package/src/License.ts +52 -0
  40. package/src/VoipCloud.ts +98 -0
  41. package/src/events.ts +23 -0
  42. package/src/index.ts +23 -0
  43. package/src/types.ts +144 -0
@@ -0,0 +1,83 @@
1
+ import { NativeModules } from 'react-native';
2
+ import { License } from './License';
3
+ import { on } from './events';
4
+ const Native = NativeModules.VoipCloud;
5
+
6
+ /**
7
+ * Top-level VoIP SDK. Workflow:
8
+ *
9
+ * 1. {@link License.set} — install a token (paste from your portal).
10
+ * 2. {@link init} — start the PJSIP endpoint.
11
+ * 3. {@link register} — get an `accountId` back.
12
+ * 4. {@link makeCall} / {@link answer} / {@link hangup}.
13
+ *
14
+ * Every method returns a Promise. License-gate failures surface as a
15
+ * Promise rejection with `code = "VC_<NAME>"` matching the
16
+ * `PJ_EVOIPCLOUD_*` enum from `voipcloud_errno.h`.
17
+ */
18
+ export const VoipCloud = {
19
+ // ---------------------- License ---------------------------------
20
+ License,
21
+ // ---------------------- Lifecycle -------------------------------
22
+ init(cfg = {}) {
23
+ return Native.init(cfg);
24
+ },
25
+ shutdown() {
26
+ return Native.shutdown();
27
+ },
28
+ // ---------------------- Accounts --------------------------------
29
+ /** Register a new SIP account. Resolves with the account id. */
30
+ register(account) {
31
+ return Native.register(account);
32
+ },
33
+ unregister(accountId) {
34
+ return Native.unregister(accountId);
35
+ },
36
+ // ---------------------- Calls -----------------------------------
37
+ /** Place an outbound call. Resolves with the new callId. */
38
+ makeCall(accountId, destinationUri) {
39
+ return Native.makeCall(accountId, destinationUri);
40
+ },
41
+ answer(callId) {
42
+ return Native.answer(callId);
43
+ },
44
+ hangup(callId) {
45
+ return Native.hangup(callId);
46
+ },
47
+ hold(callId) {
48
+ return Native.hold(callId);
49
+ },
50
+ unhold(callId) {
51
+ return Native.unhold(callId);
52
+ },
53
+ /**
54
+ * Mute / unmute the local microphone for a call. Implemented by
55
+ * stopping / starting the capture device's transmission to the
56
+ * call's active audio media — independent of `hold`. A
57
+ * `callMediaState` event fires after the toggle so the UI can
58
+ * mirror the new `isMuted` flag without polling.
59
+ */
60
+ mute(callId, on) {
61
+ return Native.mute(callId, on);
62
+ },
63
+ sendDtmf(callId, digits) {
64
+ return Native.sendDtmf(callId, digits);
65
+ },
66
+ /**
67
+ * Blind-transfer a call (SIP REFER) to a new destination. The
68
+ * local leg moves to DISCONNECTED once the remote party accepts.
69
+ */
70
+ transferCall(callId, destinationUri) {
71
+ return Native.transferCall(callId, destinationUri);
72
+ },
73
+ /**
74
+ * End every active call. Each call still emits its own
75
+ * `callState` event with state=DISCONNECTED.
76
+ */
77
+ hangupAllCalls() {
78
+ return Native.hangupAllCalls();
79
+ },
80
+ // ---------------------- Events ----------------------------------
81
+ on
82
+ };
83
+ //# sourceMappingURL=VoipCloud.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NativeModules","License","on","Native","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":"AAAA,SAASA,aAAa,QAAQ,cAAc;AAE5C,SAASC,OAAO,QAAQ,WAAW;AACnC,SAASC,EAAE,QAAQ,UAAU;AAE7B,MAAMC,MAAM,GAAGH,aAAa,CAACI,SAAS;;AAEtC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMA,SAAS,GAAG;EACrB;EACAH,OAAO;EAEP;EACAI,IAAIA,CAACC,GAAe,GAAG,CAAC,CAAC,EAAiB;IACtC,OAAOH,MAAM,CAACE,IAAI,CAACC,GAAG,CAAC;EAC3B,CAAC;EAEDC,QAAQA,CAAA,EAAkB;IACtB,OAAOJ,MAAM,CAACI,QAAQ,CAAC,CAAC;EAC5B,CAAC;EAED;EACA;EACAC,QAAQA,CAACC,OAAsB,EAAmB;IAC9C,OAAON,MAAM,CAACK,QAAQ,CAACC,OAAO,CAAC;EACnC,CAAC;EAEDC,UAAUA,CAACC,SAAiB,EAAiB;IACzC,OAAOR,MAAM,CAACO,UAAU,CAACC,SAAS,CAAC;EACvC,CAAC;EAED;EACA;EACAC,QAAQA,CAACD,SAAiB,EAAEE,cAAsB,EAAmB;IACjE,OAAOV,MAAM,CAACS,QAAQ,CAACD,SAAS,EAAEE,cAAc,CAAC;EACrD,CAAC;EAEDC,MAAMA,CAACC,MAAc,EAAiB;IAClC,OAAOZ,MAAM,CAACW,MAAM,CAACC,MAAM,CAAC;EAChC,CAAC;EAEDC,MAAMA,CAACD,MAAc,EAAiB;IAClC,OAAOZ,MAAM,CAACa,MAAM,CAACD,MAAM,CAAC;EAChC,CAAC;EAEDE,IAAIA,CAACF,MAAc,EAAiB;IAChC,OAAOZ,MAAM,CAACc,IAAI,CAACF,MAAM,CAAC;EAC9B,CAAC;EAEDG,MAAMA,CAACH,MAAc,EAAiB;IAClC,OAAOZ,MAAM,CAACe,MAAM,CAACH,MAAM,CAAC;EAChC,CAAC;EAED;AACJ;AACA;AACA;AACA;AACA;AACA;EACII,IAAIA,CAACJ,MAAc,EAAEb,EAAW,EAAiB;IAC7C,OAAOC,MAAM,CAACgB,IAAI,CAACJ,MAAM,EAAEb,EAAE,CAAC;EAClC,CAAC;EAEDkB,QAAQA,CAACL,MAAc,EAAEM,MAAc,EAAiB;IACpD,OAAOlB,MAAM,CAACiB,QAAQ,CAACL,MAAM,EAAEM,MAAM,CAAC;EAC1C,CAAC;EAED;AACJ;AACA;AACA;EACIC,YAAYA,CAACP,MAAc,EAAEF,cAAsB,EAAiB;IAChE,OAAOV,MAAM,CAACmB,YAAY,CAACP,MAAM,EAAEF,cAAc,CAAC;EACtD,CAAC;EAED;AACJ;AACA;AACA;EACIU,cAAcA,CAAA,EAAkB;IAC5B,OAAOpB,MAAM,CAACoB,cAAc,CAAC,CAAC;EAClC,CAAC;EAED;EACArB;AACJ,CAAC","ignoreList":[]}
@@ -0,0 +1,23 @@
1
+ import { NativeEventEmitter, NativeModules } from 'react-native';
2
+ const Native = NativeModules.VoipCloud;
3
+
4
+ // Lazy emitter — must point at the module to satisfy the contract
5
+ // NativeEventEmitter expects on RN 0.65+.
6
+ const emitter = Native ? new NativeEventEmitter(Native) : null;
7
+
8
+ /**
9
+ * Subscribe to a typed event. Returns a handle whose `remove()` must
10
+ * be called from a React `useEffect` cleanup to avoid leaks.
11
+ */
12
+ export function on(name, callback) {
13
+ if (!emitter) {
14
+ return {
15
+ remove: () => {}
16
+ };
17
+ }
18
+ const sub = emitter.addListener(name, callback);
19
+ return {
20
+ remove: () => sub.remove()
21
+ };
22
+ }
23
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NativeEventEmitter","NativeModules","Native","VoipCloud","emitter","on","name","callback","remove","sub","addListener"],"sourceRoot":"../../src","sources":["events.ts"],"mappings":"AAAA,SAASA,kBAAkB,EAAEC,aAAa,QAAQ,cAAc;AAGhE,MAAMC,MAAM,GAAGD,aAAa,CAACE,SAAS;;AAEtC;AACA;AACA,MAAMC,OAAO,GAAGF,MAAM,GAAG,IAAIF,kBAAkB,CAACE,MAAM,CAAC,GAAG,IAAI;;AAE9D;AACA;AACA;AACA;AACA,OAAO,SAASG,EAAEA,CACdC,IAAO,EACPC,QAAsC,EAC1B;EACZ,IAAI,CAACH,OAAO,EAAE;IACV,OAAO;MAAEI,MAAM,EAAEA,CAAA,KAAM,CAAC;IAAE,CAAC;EAC/B;EACA,MAAMC,GAAG,GAAGL,OAAO,CAACM,WAAW,CAACJ,IAAI,EAAEC,QAAgC,CAAC;EACvE,OAAO;IAAEC,MAAM,EAAEA,CAAA,KAAMC,GAAG,CAACD,MAAM,CAAC;EAAE,CAAC;AACzC","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ export { VoipCloud } from './VoipCloud';
2
+ export { License } from './License';
3
+ export { on as addEventListener } from './events';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["VoipCloud","License","on","addEventListener"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"AAAA,SAASA,SAAS,QAAQ,aAAa;AACvC,SAASC,OAAO,QAAQ,WAAW;AACnC,SAASC,EAAE,IAAIC,gBAAgB,QAAQ,UAAU","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../src","sources":["types.ts"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,31 @@
1
+ import type { LicenseStatus } from './types';
2
+ /**
3
+ * License management — all SDK functionality is gated behind a valid
4
+ * token. Customers paste their token (issued by your server) into the
5
+ * app; the app calls {@link set} once at startup and the SDK handles
6
+ * everything else, including periodic refresh.
7
+ */
8
+ export declare const License: {
9
+ /**
10
+ * Apply a license token. Resolves on success, rejects with one of
11
+ * the granular `VC_*` codes if the token is malformed, signed by
12
+ * the wrong key, expired, suspended, or for a different app bundle.
13
+ *
14
+ * Side-effect: if the token's payload includes a `ref` field, the
15
+ * background refresher is started (or restarted with the new URL)
16
+ * automatically — the consumer never types a refresh URL.
17
+ */
18
+ set(token: string): Promise<void>;
19
+ /**
20
+ * Query the current license state. Safe to call before `set`
21
+ * (returns valid=false, status="unset").
22
+ */
23
+ status(): Promise<LicenseStatus>;
24
+ /**
25
+ * Cancel the background refresher. Optional: most apps will
26
+ * never call this. Useful when the user logs out or the SDK is
27
+ * being torn down and you do not want a long-running daemon
28
+ * thread sitting in the process.
29
+ */
30
+ stopRefresh(): Promise<void>;
31
+ };
@@ -0,0 +1,52 @@
1
+ import type { AccountConfig, InitConfig } from './types';
2
+ import { on } from './events';
3
+ /**
4
+ * Top-level VoIP SDK. Workflow:
5
+ *
6
+ * 1. {@link License.set} — install a token (paste from your portal).
7
+ * 2. {@link init} — start the PJSIP endpoint.
8
+ * 3. {@link register} — get an `accountId` back.
9
+ * 4. {@link makeCall} / {@link answer} / {@link hangup}.
10
+ *
11
+ * Every method returns a Promise. License-gate failures surface as a
12
+ * Promise rejection with `code = "VC_<NAME>"` matching the
13
+ * `PJ_EVOIPCLOUD_*` enum from `voipcloud_errno.h`.
14
+ */
15
+ export declare const VoipCloud: {
16
+ License: {
17
+ set(token: string): Promise<void>;
18
+ status(): Promise<import("./types").LicenseStatus>;
19
+ stopRefresh(): Promise<void>;
20
+ };
21
+ init(cfg?: InitConfig): Promise<void>;
22
+ shutdown(): Promise<void>;
23
+ /** Register a new SIP account. Resolves with the account id. */
24
+ register(account: AccountConfig): Promise<number>;
25
+ unregister(accountId: number): Promise<void>;
26
+ /** Place an outbound call. Resolves with the new callId. */
27
+ makeCall(accountId: number, destinationUri: string): Promise<number>;
28
+ answer(callId: number): Promise<void>;
29
+ hangup(callId: number): Promise<void>;
30
+ hold(callId: number): Promise<void>;
31
+ unhold(callId: number): Promise<void>;
32
+ /**
33
+ * Mute / unmute the local microphone for a call. Implemented by
34
+ * stopping / starting the capture device's transmission to the
35
+ * call's active audio media — independent of `hold`. A
36
+ * `callMediaState` event fires after the toggle so the UI can
37
+ * mirror the new `isMuted` flag without polling.
38
+ */
39
+ mute(callId: number, on: boolean): Promise<void>;
40
+ sendDtmf(callId: number, digits: string): Promise<void>;
41
+ /**
42
+ * Blind-transfer a call (SIP REFER) to a new destination. The
43
+ * local leg moves to DISCONNECTED once the remote party accepts.
44
+ */
45
+ transferCall(callId: number, destinationUri: string): Promise<void>;
46
+ /**
47
+ * End every active call. Each call still emits its own
48
+ * `callState` event with state=DISCONNECTED.
49
+ */
50
+ hangupAllCalls(): Promise<void>;
51
+ on: typeof on;
52
+ };
@@ -0,0 +1,6 @@
1
+ import type { EventMap, Subscription } from './types';
2
+ /**
3
+ * Subscribe to a typed event. Returns a handle whose `remove()` must
4
+ * be called from a React `useEffect` cleanup to avoid leaks.
5
+ */
6
+ export declare function on<K extends keyof EventMap>(name: K, callback: (event: EventMap[K]) => void): Subscription;
@@ -0,0 +1,4 @@
1
+ export { VoipCloud } from './VoipCloud';
2
+ export { License } from './License';
3
+ export { on as addEventListener } from './events';
4
+ export type { Transport, InitConfig, AccountConfig, LicenseStatus, RegistrationStateEvent, IncomingCallEvent, CallState, CallStateEvent, CallMediaStateEvent, CallHoldEvent, CallUnholdEvent, CallMuteEvent, CallDtmfSentEvent, CallTransferEvent, CallTransferStatusEvent, IncomingDtmfEvent, EventMap, Subscription, } from './types';
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Public types for react-native-voipcloud.
3
+ * Keep these stable across versions — they form the API contract.
4
+ */
5
+ export type Transport = 'UDP' | 'TCP' | 'TLS';
6
+ export interface InitConfig {
7
+ /** STUN server hostname, e.g. "stun.l.google.com:19302". Optional. */
8
+ stunServer?: string;
9
+ /** User-Agent header value advertised to the SIP server. */
10
+ userAgent?: string;
11
+ /** Default log level 0–6 (PJSIP convention; 4 = info). */
12
+ logLevel?: number;
13
+ }
14
+ export interface AccountConfig {
15
+ /** AOR — e.g. "sip:alice@example.com". */
16
+ uri: string;
17
+ /**
18
+ * Registrar URI — e.g. "sip:example.com" or
19
+ * "sip:example.com:5061;transport=tls". The transport is taken from
20
+ * the `;transport=` parameter on this URI; if absent, PJSIP defaults
21
+ * to UDP.
22
+ */
23
+ registrar: string;
24
+ /** Auth username (often the same as the local part of `uri`). */
25
+ username: string;
26
+ /** Auth password (will be sent only in challenge response, never logged). */
27
+ password: string;
28
+ /**
29
+ * Hint for the transport you intend to use. Informational only —
30
+ * the actual transport is resolved from the `registrar` URI. Keep
31
+ * the two in sync to avoid surprises.
32
+ */
33
+ transport: Transport;
34
+ }
35
+ export interface LicenseStatus {
36
+ /** True if voipcloud_license_is_valid() returned 1 at call time. */
37
+ valid: boolean;
38
+ /** Server-side label: "active" | "trial" | "suspended" | "expired" | "unset". */
39
+ status: string;
40
+ /** Customer identifier embedded in the token. */
41
+ customerId: string;
42
+ /** Concurrent-call ceiling allowed by the license. */
43
+ ccu: number;
44
+ /** Current count of active calls held by the gate. */
45
+ currentCalls: number;
46
+ /** Token expiry (unix seconds). 0 if unbounded. */
47
+ expiry: number;
48
+ /** Last clock-skew anchor pushed by the refresher (unix seconds). 0 if none yet. */
49
+ serverTime: number;
50
+ }
51
+ export interface RegistrationStateEvent {
52
+ accountId: number;
53
+ /** SIP response code (200 = registered, 401/407 = auth, etc.). */
54
+ code: number;
55
+ /** Free-form reason string. */
56
+ reason: string;
57
+ }
58
+ export interface IncomingCallEvent {
59
+ accountId: number;
60
+ callId: number;
61
+ /** Remote-party display URI. */
62
+ from: string;
63
+ /** Local AOR. */
64
+ to: string;
65
+ }
66
+ export type CallState = 'NULL' | 'CALLING' | 'INCOMING' | 'EARLY' | 'CONNECTING' | 'CONFIRMED' | 'DISCONNECTED';
67
+ export interface CallStateEvent {
68
+ callId: number;
69
+ state: CallState;
70
+ code: number;
71
+ reason: string;
72
+ }
73
+ export interface CallMediaStateEvent {
74
+ callId: number;
75
+ hasAudio: boolean;
76
+ isHold: boolean;
77
+ isMuted: boolean;
78
+ }
79
+ /** Fired right after `hold(callId)` succeeds (re-INVITE sent). */
80
+ export interface CallHoldEvent {
81
+ callId: number;
82
+ }
83
+ /** Fired right after `unhold(callId)` succeeds. */
84
+ export interface CallUnholdEvent {
85
+ callId: number;
86
+ }
87
+ /** Fired right after `mute(callId, isMuted)` succeeds. */
88
+ export interface CallMuteEvent {
89
+ callId: number;
90
+ isMuted: boolean;
91
+ }
92
+ /** Fired right after `sendDtmf(callId, digits)` succeeds. */
93
+ export interface CallDtmfSentEvent {
94
+ callId: number;
95
+ digits: string;
96
+ }
97
+ /** Fired right after `transferCall(callId, destinationUri)` sends the REFER. */
98
+ export interface CallTransferEvent {
99
+ callId: number;
100
+ destinationUri: string;
101
+ }
102
+ /**
103
+ * Multi-stage progress for a transfer we initiated. Fires once per
104
+ * NOTIFY received from the remote — final stage has `finalNotify=true`
105
+ * and a 2xx statusCode (success) or a 4xx-6xx (failure).
106
+ */
107
+ export interface CallTransferStatusEvent {
108
+ callId: number;
109
+ statusCode: number;
110
+ reason: string;
111
+ finalNotify: boolean;
112
+ }
113
+ /** RFC 2833 DTMF digit detected on the inbound RTP stream. */
114
+ export interface IncomingDtmfEvent {
115
+ callId: number;
116
+ digit: string;
117
+ /** Duration in PJSIP timer ticks (~ ms with default sampling). */
118
+ duration: number;
119
+ }
120
+ export interface EventMap {
121
+ registrationState: RegistrationStateEvent;
122
+ incomingCall: IncomingCallEvent;
123
+ callState: CallStateEvent;
124
+ callMediaState: CallMediaStateEvent;
125
+ callHold: CallHoldEvent;
126
+ callUnhold: CallUnholdEvent;
127
+ callMute: CallMuteEvent;
128
+ callDtmfSent: CallDtmfSentEvent;
129
+ callTransfer: CallTransferEvent;
130
+ callTransferStatus: CallTransferStatusEvent;
131
+ incomingDtmf: IncomingDtmfEvent;
132
+ }
133
+ /** Subscription handle returned by VoipCloud.on(...). */
134
+ export interface Subscription {
135
+ remove(): void;
136
+ }
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@voipsdk/react-native-voipcloud",
3
+ "version": "0.2.1",
4
+ "description": "React Native VoIP SDK backed by PJSIP, license-gated by VoipCloud.",
5
+ "main": "lib/commonjs/index.js",
6
+ "module": "lib/module/index.js",
7
+ "react-native": "src/index.ts",
8
+ "types": "lib/typescript/index.d.ts",
9
+ "source": "src/index.ts",
10
+ "files": [
11
+ "src",
12
+ "lib",
13
+ "android",
14
+ "ios",
15
+ "react-native-voipcloud.podspec",
16
+ "README.md",
17
+ "!**/__tests__",
18
+ "!**/.*"
19
+ ],
20
+ "scripts": {
21
+ "prepare": "bob build && yarn build:types",
22
+ "build:types": "tsc --project tsconfig.build.json --outDir lib/typescript --emitDeclarationOnly --declaration",
23
+ "clean": "rm -rf lib",
24
+ "pack:tgz": "yarn pack --out ./%s-%v.tgz"
25
+ },
26
+ "keywords": [
27
+ "react-native",
28
+ "voip",
29
+ "sip",
30
+ "sip-client",
31
+ "pjsip",
32
+ "android",
33
+ "telephony",
34
+ "calling",
35
+ "webrtc",
36
+ "voipcloud"
37
+ ],
38
+ "author": {
39
+ "name": "VoipCloud",
40
+ "email": "htt.bkap@gmail.com"
41
+ },
42
+ "license": "UNLICENSED",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/hoangdx/react-native-voipcloud.git"
46
+ },
47
+ "bugs": {
48
+ "url": "https://github.com/hoangdx/react-native-voipcloud/issues"
49
+ },
50
+ "homepage": "https://github.com/hoangdx/react-native-voipcloud#readme",
51
+ "engines": {
52
+ "node": ">=18"
53
+ },
54
+ "peerDependencies": {
55
+ "react": ">=17.0.0",
56
+ "react-native": ">=0.70.0"
57
+ },
58
+ "devDependencies": {
59
+ "@types/react": "^18.2.0",
60
+ "react": "18.2.0",
61
+ "react-native": "0.74.0",
62
+ "react-native-builder-bob": "^0.20.0",
63
+ "typescript": "^5.3.0"
64
+ },
65
+ "react-native-builder-bob": {
66
+ "source": "src",
67
+ "output": "lib",
68
+ "targets": [
69
+ "commonjs",
70
+ "module"
71
+ ]
72
+ },
73
+ "publishConfig": {
74
+ "access": "public"
75
+ }
76
+ }
@@ -0,0 +1,16 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "react-native-voipcloud"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = "https://github.com/hoangdx/react-native-voipcloud"
10
+ s.license = "UNLICENSED"
11
+ s.authors = { "VoipCloud" => "htt.bkap@gmail.com" }
12
+ s.platforms = { :ios => "13.0" }
13
+ s.source = { :git => "https://github.com/hoangdx/react-native-voipcloud.git", :tag => "#{s.version}" }
14
+ s.source_files = "ios/**/*.{h,m,mm,swift}"
15
+ s.dependency "React-Core"
16
+ end
package/src/License.ts ADDED
@@ -0,0 +1,52 @@
1
+ import { NativeModules } from 'react-native';
2
+ import type { LicenseStatus } from './types';
3
+
4
+ const Native = NativeModules.VoipCloud;
5
+
6
+ if (!Native) {
7
+ // Defer the error: only thrown if the consumer actually calls a
8
+ // method, so importing the package never crashes at load time.
9
+ console.warn(
10
+ '[react-native-voipcloud] Native module not linked. ' +
11
+ 'Did you rebuild the Android app after adding the package?',
12
+ );
13
+ }
14
+
15
+ /**
16
+ * License management — all SDK functionality is gated behind a valid
17
+ * token. Customers paste their token (issued by your server) into the
18
+ * app; the app calls {@link set} once at startup and the SDK handles
19
+ * everything else, including periodic refresh.
20
+ */
21
+ export const License = {
22
+ /**
23
+ * Apply a license token. Resolves on success, rejects with one of
24
+ * the granular `VC_*` codes if the token is malformed, signed by
25
+ * the wrong key, expired, suspended, or for a different app bundle.
26
+ *
27
+ * Side-effect: if the token's payload includes a `ref` field, the
28
+ * background refresher is started (or restarted with the new URL)
29
+ * automatically — the consumer never types a refresh URL.
30
+ */
31
+ set(token: string): Promise<void> {
32
+ return Native.setLicense(token);
33
+ },
34
+
35
+ /**
36
+ * Query the current license state. Safe to call before `set`
37
+ * (returns valid=false, status="unset").
38
+ */
39
+ status(): Promise<LicenseStatus> {
40
+ return Native.getLicenseStatus();
41
+ },
42
+
43
+ /**
44
+ * Cancel the background refresher. Optional: most apps will
45
+ * never call this. Useful when the user logs out or the SDK is
46
+ * being torn down and you do not want a long-running daemon
47
+ * thread sitting in the process.
48
+ */
49
+ stopRefresh(): Promise<void> {
50
+ return Native.stopLicenseRefresh();
51
+ },
52
+ };
@@ -0,0 +1,98 @@
1
+ import { NativeModules } from 'react-native';
2
+ import type { AccountConfig, InitConfig } from './types';
3
+ import { License } from './License';
4
+ import { on } from './events';
5
+
6
+ const Native = NativeModules.VoipCloud;
7
+
8
+ /**
9
+ * Top-level VoIP SDK. Workflow:
10
+ *
11
+ * 1. {@link License.set} — install a token (paste from your portal).
12
+ * 2. {@link init} — start the PJSIP endpoint.
13
+ * 3. {@link register} — get an `accountId` back.
14
+ * 4. {@link makeCall} / {@link answer} / {@link hangup}.
15
+ *
16
+ * Every method returns a Promise. License-gate failures surface as a
17
+ * Promise rejection with `code = "VC_<NAME>"` matching the
18
+ * `PJ_EVOIPCLOUD_*` enum from `voipcloud_errno.h`.
19
+ */
20
+ export const VoipCloud = {
21
+ // ---------------------- License ---------------------------------
22
+ License,
23
+
24
+ // ---------------------- Lifecycle -------------------------------
25
+ init(cfg: InitConfig = {}): Promise<void> {
26
+ return Native.init(cfg);
27
+ },
28
+
29
+ shutdown(): Promise<void> {
30
+ return Native.shutdown();
31
+ },
32
+
33
+ // ---------------------- Accounts --------------------------------
34
+ /** Register a new SIP account. Resolves with the account id. */
35
+ register(account: AccountConfig): Promise<number> {
36
+ return Native.register(account);
37
+ },
38
+
39
+ unregister(accountId: number): Promise<void> {
40
+ return Native.unregister(accountId);
41
+ },
42
+
43
+ // ---------------------- Calls -----------------------------------
44
+ /** Place an outbound call. Resolves with the new callId. */
45
+ makeCall(accountId: number, destinationUri: string): Promise<number> {
46
+ return Native.makeCall(accountId, destinationUri);
47
+ },
48
+
49
+ answer(callId: number): Promise<void> {
50
+ return Native.answer(callId);
51
+ },
52
+
53
+ hangup(callId: number): Promise<void> {
54
+ return Native.hangup(callId);
55
+ },
56
+
57
+ hold(callId: number): Promise<void> {
58
+ return Native.hold(callId);
59
+ },
60
+
61
+ unhold(callId: number): Promise<void> {
62
+ return Native.unhold(callId);
63
+ },
64
+
65
+ /**
66
+ * Mute / unmute the local microphone for a call. Implemented by
67
+ * stopping / starting the capture device's transmission to the
68
+ * call's active audio media — independent of `hold`. A
69
+ * `callMediaState` event fires after the toggle so the UI can
70
+ * mirror the new `isMuted` flag without polling.
71
+ */
72
+ mute(callId: number, on: boolean): Promise<void> {
73
+ return Native.mute(callId, on);
74
+ },
75
+
76
+ sendDtmf(callId: number, digits: string): Promise<void> {
77
+ return Native.sendDtmf(callId, digits);
78
+ },
79
+
80
+ /**
81
+ * Blind-transfer a call (SIP REFER) to a new destination. The
82
+ * local leg moves to DISCONNECTED once the remote party accepts.
83
+ */
84
+ transferCall(callId: number, destinationUri: string): Promise<void> {
85
+ return Native.transferCall(callId, destinationUri);
86
+ },
87
+
88
+ /**
89
+ * End every active call. Each call still emits its own
90
+ * `callState` event with state=DISCONNECTED.
91
+ */
92
+ hangupAllCalls(): Promise<void> {
93
+ return Native.hangupAllCalls();
94
+ },
95
+
96
+ // ---------------------- Events ----------------------------------
97
+ on,
98
+ };
package/src/events.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { NativeEventEmitter, NativeModules } from 'react-native';
2
+ import type { EventMap, Subscription } from './types';
3
+
4
+ const Native = NativeModules.VoipCloud;
5
+
6
+ // Lazy emitter — must point at the module to satisfy the contract
7
+ // NativeEventEmitter expects on RN 0.65+.
8
+ const emitter = Native ? new NativeEventEmitter(Native) : null;
9
+
10
+ /**
11
+ * Subscribe to a typed event. Returns a handle whose `remove()` must
12
+ * be called from a React `useEffect` cleanup to avoid leaks.
13
+ */
14
+ export function on<K extends keyof EventMap>(
15
+ name: K,
16
+ callback: (event: EventMap[K]) => void,
17
+ ): Subscription {
18
+ if (!emitter) {
19
+ return { remove: () => {} };
20
+ }
21
+ const sub = emitter.addListener(name, callback as (e: unknown) => void);
22
+ return { remove: () => sub.remove() };
23
+ }
package/src/index.ts ADDED
@@ -0,0 +1,23 @@
1
+ export { VoipCloud } from './VoipCloud';
2
+ export { License } from './License';
3
+ export { on as addEventListener } from './events';
4
+ export type {
5
+ Transport,
6
+ InitConfig,
7
+ AccountConfig,
8
+ LicenseStatus,
9
+ RegistrationStateEvent,
10
+ IncomingCallEvent,
11
+ CallState,
12
+ CallStateEvent,
13
+ CallMediaStateEvent,
14
+ CallHoldEvent,
15
+ CallUnholdEvent,
16
+ CallMuteEvent,
17
+ CallDtmfSentEvent,
18
+ CallTransferEvent,
19
+ CallTransferStatusEvent,
20
+ IncomingDtmfEvent,
21
+ EventMap,
22
+ Subscription,
23
+ } from './types';