├── .eslintrc
├── .gitignore
├── .jsdoc.json
├── .libs.lock
├── LICENSE.txt
├── README.md
├── android
├── .gitignore
├── .npmignore
├── .project
├── .settings
│ └── org.eclipse.buildship.core.prefs
├── android.iml
├── build.gradle
├── react-native-pjsip.iml
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── ic_notif-web.png
│ ├── java
│ └── com
│ │ └── carusto
│ │ └── ReactNativePjSip
│ │ ├── PjActions.java
│ │ ├── PjSipAccount.java
│ │ ├── PjSipBroadcastEmiter.java
│ │ ├── PjSipBroadcastReceiver.java
│ │ ├── PjSipCall.java
│ │ ├── PjSipLogWriter.java
│ │ ├── PjSipMessage.java
│ │ ├── PjSipModule.java
│ │ ├── PjSipModulePackage.java
│ │ ├── PjSipPreviewVideo.java
│ │ ├── PjSipPreviewVideoViewManager.java
│ │ ├── PjSipRemoteVideo.java
│ │ ├── PjSipRemoteVideoViewManager.java
│ │ ├── PjSipService.java
│ │ ├── PjSipUtils.java
│ │ ├── PjSipVideo.java
│ │ ├── PjSipVideoMediaChange.java
│ │ ├── PjSipVideoWindowHandler.java
│ │ ├── dto
│ │ ├── AccountConfigurationDTO.java
│ │ ├── CallSettingsDTO.java
│ │ ├── ServiceConfigurationDTO.java
│ │ └── SipMessageDTO.java
│ │ └── utils
│ │ └── ArgumentUtils.java
│ ├── main.iml
│ └── res
│ ├── drawable-hdpi
│ └── ic_notif.png
│ ├── drawable-mdpi
│ └── ic_notif.png
│ ├── drawable-xhdpi
│ └── ic_notif.png
│ ├── drawable-xxhdpi
│ └── ic_notif.png
│ └── raw
│ ├── ring.wav
│ └── ringback.wav
├── build
├── Account.d.ts
├── Account.js
├── Account.js.map
├── AccountRegistration.d.ts
├── AccountRegistration.js
├── AccountRegistration.js.map
├── Call.d.ts
├── Call.js
├── Call.js.map
├── Endpoint.d.ts
├── Endpoint.js
├── Endpoint.js.map
├── Message.d.ts
├── Message.js
├── Message.js.map
├── PreviewVideoView.d.ts
├── PreviewVideoView.js
├── PreviewVideoView.js.map
├── RemoteVideoView.d.ts
├── RemoteVideoView.js
├── RemoteVideoView.js.map
├── index.d.ts
├── index.js
└── index.js.map
├── docs
├── Account.js.html
├── AccountRegistration.js.html
├── Call.js.html
├── Endpoint.js.html
├── Message.js.html
├── fonts
│ ├── OpenSans-Bold-webfont.eot
│ ├── OpenSans-Bold-webfont.svg
│ ├── OpenSans-Bold-webfont.woff
│ ├── OpenSans-BoldItalic-webfont.eot
│ ├── OpenSans-BoldItalic-webfont.svg
│ ├── OpenSans-BoldItalic-webfont.woff
│ ├── OpenSans-Italic-webfont.eot
│ ├── OpenSans-Italic-webfont.svg
│ ├── OpenSans-Italic-webfont.woff
│ ├── OpenSans-Light-webfont.eot
│ ├── OpenSans-Light-webfont.svg
│ ├── OpenSans-Light-webfont.woff
│ ├── OpenSans-LightItalic-webfont.eot
│ ├── OpenSans-LightItalic-webfont.svg
│ ├── OpenSans-LightItalic-webfont.woff
│ ├── OpenSans-Regular-webfont.eot
│ ├── OpenSans-Regular-webfont.svg
│ ├── OpenSans-Regular-webfont.woff
│ ├── OpenSans-Semibold-webfont.eot
│ ├── OpenSans-Semibold-webfont.svg
│ ├── OpenSans-Semibold-webfont.ttf
│ ├── OpenSans-Semibold-webfont.woff
│ ├── OpenSans-SemiboldItalic-webfont.eot
│ ├── OpenSans-SemiboldItalic-webfont.svg
│ ├── OpenSans-SemiboldItalic-webfont.ttf
│ └── OpenSans-SemiboldItalic-webfont.woff
├── global.html
├── index.html
├── module.exports.html
├── scripts
│ ├── linenumber.js
│ └── prettify
│ │ ├── Apache-License-2.0.txt
│ │ ├── lang-css.js
│ │ └── prettify.js
└── styles
│ ├── jsdoc-default.css
│ ├── prettify-jsdoc.css
│ └── prettify-tomorrow.css
├── index.js
├── ios
├── .gitignore
├── .npmignore
├── RTCPjSip.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── RTCPjSip.xcscmblueprint
│ │ └── xcuserdata
│ │ │ ├── datso.xcuserdatad
│ │ │ ├── UserInterfaceState.xcuserstate
│ │ │ └── xcschemes
│ │ │ │ └── xcschememanagement.plist
│ │ │ ├── vadim.xcuserdatad
│ │ │ ├── UserInterfaceState.xcuserstate
│ │ │ └── xcschemes
│ │ │ │ └── xcschememanagement.plist
│ │ │ └── vruban.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ ├── datso.xcuserdatad
│ │ └── xcschemes
│ │ │ ├── RTCPjSip.xcscheme
│ │ │ └── xcschememanagement.plist
│ │ ├── vadim.xcuserdatad
│ │ └── xcschemes
│ │ │ ├── RTCPjSip.xcscheme
│ │ │ └── xcschememanagement.plist
│ │ └── vruban.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── RTCPjSip
│ ├── PjSipAccount.h
│ ├── PjSipAccount.m
│ ├── PjSipCall.h
│ ├── PjSipCall.m
│ ├── PjSipEndpoint.h
│ ├── PjSipEndpoint.m
│ ├── PjSipLocalVideoViewManager.h
│ ├── PjSipLocalVideoViewManager.m
│ ├── PjSipMessage.h
│ ├── PjSipMessage.m
│ ├── PjSipModule.h
│ ├── PjSipModule.m
│ ├── PjSipPreviewVideoViewManager.h
│ ├── PjSipPreviewVideoViewManager.m
│ ├── PjSipRemoteVideoViewManager.h
│ ├── PjSipRemoteVideoViewManager.m
│ ├── PjSipUtil.h
│ ├── PjSipUtil.m
│ ├── PjSipVideo.h
│ ├── PjSipVideo.m
│ ├── PjSipVideoViewManager.h
│ └── PjSipVideoViewManager.m
├── libs.sh
├── package.json
├── react-native-xsip.podspec
├── src
├── Account.ts
├── AccountRegistration.ts
├── Call.ts
├── Endpoint.ts
├── Message.ts
├── PreviewVideoView.ts
├── RemoteVideoView.ts
└── index.ts
├── tsconfig.json
└── wiki
├── accounts.md
├── android_notification_example.png
├── android_sip_background.md
├── calls.md
├── installation_android.md
├── installation_ios.md
├── ios_push_notifications_callkit.md
├── settings.md
└── startup.md
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "parser": "babel-eslint",
4 | "plugins": [
5 | "react-native"
6 | ],
7 | "rules": {
8 | "semi": ["error", "never"]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | dist
4 | ios/VialerPJSIP.framework
5 | react-native-pjsip-builder-main
6 | *.log
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 | lerna-debug.log*
11 | yarn.lock
12 | .libs.lock
13 | package-lock.json
14 | # Diagnostic reports (https://nodejs.org/api/report.html)
15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
16 |
17 | # Runtime data
18 | pids
19 | *.pid
20 | *.seed
21 | *.pid.lock
22 |
23 | # Directory for instrumented libs generated by jscoverage/JSCover
24 | lib-cov
25 |
26 | # Coverage directory used by tools like istanbul
27 | coverage
28 | *.lcov
29 |
30 | # nyc test coverage
31 | .nyc_output
32 |
33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
34 | .grunt
35 |
36 | # Bower dependency directory (https://bower.io/)
37 | bower_components
38 |
39 | # node-waf configuration
40 | .lock-wscript
41 |
42 | # Compiled binary addons (https://nodejs.org/api/addons.html)
43 | build/Release
44 |
45 | # Dependency directories
46 | node_modules/
47 | jspm_packages/
48 |
49 | # TypeScript v1 declaration files
50 | typings/
51 |
52 | # TypeScript cache
53 | *.tsbuildinfo
54 |
55 | # Optional npm cache directory
56 | .npm
57 |
58 | # Optional eslint cache
59 | .eslintcache
60 |
61 | # Microbundle cache
62 | .rpt2_cache/
63 | .rts2_cache_cjs/
64 | .rts2_cache_es/
65 | .rts2_cache_umd/
66 |
67 | # Optional REPL history
68 | .node_repl_history
69 |
70 | # Output of 'npm pack'
71 | *.tgz
72 |
73 | # Yarn Integrity file
74 | .yarn-integrity
75 |
76 | # dotenv environment variables file
77 | .env
78 | .env.test
79 |
80 | # parcel-bundler cache (https://parceljs.org/)
81 | .cache
82 |
83 | # Next.js build output
84 | .next
85 |
86 | # Nuxt.js build / generate output
87 | .nuxt
88 |
89 | # Gatsby files
90 | .cache/
91 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
92 | # https://nextjs.org/blog/next-9-1#public-directory-support
93 | # public
94 |
95 | # vuepress build output
96 | .vuepress/dist
97 |
98 | # Serverless directories
99 | .serverless/
100 |
101 | # FuseBox cache
102 | .fusebox/
103 |
104 | # DynamoDB Local files
105 | .dynamodb/
106 |
107 | # TernJS port file
108 | .tern-port
109 |
--------------------------------------------------------------------------------
/.jsdoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "tags": {
3 | "allowUnknownTags": true,
4 | "dictionaries": ["jsdoc"]
5 | },
6 | "source": {
7 | "include": ["lib", "package.json", "README.md"],
8 | "includePattern": ".js$",
9 | "excludePattern": "(node_modules/|docs)"
10 | },
11 | "plugins": [
12 | "plugins/markdown"
13 | ],
14 | "templates": {
15 | "cleverLinks": false,
16 | "monospaceLinks": true,
17 | "useLongnameInNav": false,
18 | "showInheritedInNav": true
19 | },
20 | "opts": {
21 | "destination": "./docs/",
22 | "encoding": "utf8",
23 | "private": true,
24 | "recurse": true,
25 | "template": "./node_modules/minami"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/.libs.lock:
--------------------------------------------------------------------------------
1 | v2.9.0
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-xsip
2 | clone from https://github.com/datso/react-native-pjsip but improve many things
3 |
4 | # react-native-pjsip
5 |
6 | A [PJSIP](http://www.pjsip.org/) module for React Native.
7 |
8 | ## Support
9 | - Currently support for iOS and Android.
10 | - Support video and audio communication.
11 | - Ability to use Callkit and PushNotifications.
12 | - You can use it to build an iOS/Android app that can communicate with SIP server.
13 | - Android version is based on [react-native-pjsip-builder](https://github.com/datso/react-native-pjsip-builder)
14 | - iOS version is based on [Vialer-pjsip-iOS](https://github.com/VoIPGRID/Vialer-pjsip-iOS)
15 |
16 | ## Installation
17 |
18 | - [iOS](https://github.com/datso/react-native-pjsip/blob/master/docs/installation_ios.md)
19 | - [Android](https://github.com/datso/react-native-pjsip/blob/master/docs/installation_android.md)
20 |
21 | ## Usage
22 |
23 | First of all you have to initialize module to be able to work with it.
24 |
25 | There are some interesting moment in initialization.
26 | When application goes to background, PJSIP module is still working and able to receive calls, but your javascipt is totally suspended.
27 | When User open your application, javascript start to work and now your js application need to know what status have your account or may be you have pending incoming call.
28 |
29 | So thats why first step should call start method for pjsip module.
30 |
31 | ```javascript
32 | import {Endpoint} from 'react-native-xsip'
33 |
34 | let endpoint = new Endpoint();
35 | let state = await endpoint.start(); // List of available accounts and calls when RN context is started, could not be empty because Background service is working on Android
36 | let {accounts, calls, settings, connectivity} = state;
37 |
38 | // Subscribe to endpoint events
39 | endpoint.on("registration_changed", (account) => {});
40 | endpoint.on("connectivity_changed", (online) => {});
41 | endpoint.on("call_received", (call) => {});
42 | endpoint.on("call_changed", (call) => {});
43 | endpoint.on("call_terminated", (call) => {});
44 | endpoint.on("call_screen_locked", (call) => {}); // Android only
45 | ```
46 |
47 | Account creating is pretty strainghforward.
48 |
49 | ```javascript
50 | let configuration = {
51 | "name": "John",
52 | "username": "sip_username",
53 | "domain": "pbx.carusto.com",
54 | "password": "****",
55 | "proxy": null,
56 | "transport": null, // Default TCP
57 | "regServer": null, // Default wildcard
58 | "regTimeout": null // Default 3600
59 | "regHeaders": {
60 | "X-Custom-Header": "Value"
61 | },
62 | "regContactParams": ";unique-device-token-id=XXXXXXXXX"
63 | };
64 | endpoint.createAccount(configuration).then((account) => {
65 | console.log("Account created", account);
66 | });
67 |
68 | ```
69 |
70 | To be able to make a call first of all you should createAccount, and pass account instance into Endpoint.makeCall function.
71 | This function will return a promise that will be resolved when PjSIP initializes the call.
72 |
73 | ```javascript
74 | let options = {
75 | headers: {
76 | "P-Assserted-Identity": "Header example",
77 | "X-UA": "React native"
78 | }
79 | }
80 |
81 | let call = await endpoint.makeCall(account, destination, options);
82 | call.getId() // Use this id to detect changes and make actions
83 |
84 | endpoint.addListener("call_changed", (newCall) => {
85 | if (call.getId() === newCall.getId()) {
86 | // Our call changed, do smth.
87 | }
88 | }
89 | endpoint.addListener("call_terminated", (newCall) => {
90 | if (call.getId() === newCall.getId()) {
91 | // Our call terminated
92 | }
93 | }
94 | ```
95 |
96 | ## API
97 |
98 | 1. [Startup](https://github.com/mcjambi/react-native-xsip/tree/main/docs)
99 | 2. [Accounts](https://github.com/mcjambi/react-native-xsip/tree/main/docs)
100 | 3. [Calls](https://github.com/mcjambi/react-native-xsip/tree/main/docs)
101 | 4. [Settings](https://github.com/mcjambi/react-native-xsip/tree/main/docs)
102 |
103 | ## Contact me
104 |
105 | - If it is not working for a resion, please contact me at mcjambi(at)gmail.com or jamvietdotcom(at)gmail.com
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle.properties
2 | gradle/
3 | gradlew
4 | gradlew.bat
5 | local.properties
6 | .gradle
7 | .idea
8 | DS_Store
9 |
--------------------------------------------------------------------------------
/android/.npmignore:
--------------------------------------------------------------------------------
1 | gradle.properties
2 | gradle/
3 | gradlew
4 | gradlew.bat
5 | local.properties
6 | .gradle
7 | .idea
8 | DS_Store
9 |
--------------------------------------------------------------------------------
/android/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | android
4 | Project android created by Buildship.
5 |
6 |
7 |
8 |
9 | org.eclipse.buildship.core.gradleprojectbuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.buildship.core.gradleprojectnature
16 |
17 |
18 |
19 | 1601449854118
20 |
21 | 30
22 |
23 | org.eclipse.core.resources.regexFilterMatcher
24 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/android/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | arguments=
2 | auto.sync=false
3 | build.scans.enabled=false
4 | connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(7.4.2))
5 | connection.project.dir=
6 | eclipse.preferences.version=1
7 | gradle.user.home=
8 | java.home=/Library/Java/JavaVirtualMachines/jdk-16.0.2.jdk/Contents/Home
9 | jvm.arguments=
10 | offline.mode=false
11 | override.workspace.settings=true
12 | show.console.view=true
13 | show.executions.view=true
14 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:1.5.0'
10 | }
11 | }
12 |
13 | repositories {
14 | google()
15 | jcenter()
16 | }
17 |
18 | android {
19 | compileSdkVersion 29
20 | buildToolsVersion "29.0.1"
21 |
22 | defaultConfig {
23 | minSdkVersion 16
24 | targetSdkVersion 26
25 | versionCode 1
26 | versionName "1.0"
27 | ndk {
28 | abiFilters "armeabi-v7a", "x86"
29 | }
30 | }
31 | }
32 |
33 | dependencies {
34 | implementation 'com.facebook.react:react-native:+'
35 | implementation "com.google.code.gson:gson:2.+"
36 | implementation fileTree(dir: 'libs', include: ['*.jar'])
37 | }
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/android/src/main/ic_notif-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/android/src/main/ic_notif-web.png
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipAccount.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | import com.carusto.ReactNativePjSip.dto.AccountConfigurationDTO;
4 | import org.json.JSONObject;
5 | import org.pjsip.pjsua2.Account;
6 | import org.pjsip.pjsua2.OnIncomingCallParam;
7 | import org.pjsip.pjsua2.OnInstantMessageParam;
8 | import org.pjsip.pjsua2.OnRegStateParam;
9 |
10 | public class PjSipAccount extends Account {
11 |
12 | private static String TAG = "PjSipAccount";
13 |
14 | /**
15 | * Last registration reason.
16 | */
17 | private String reason;
18 |
19 | private PjSipService service;
20 |
21 | private AccountConfigurationDTO configuration;
22 |
23 | private Integer transportId;
24 |
25 | public PjSipAccount(PjSipService service, int transportId, AccountConfigurationDTO configuration) {
26 | this.service = service;
27 | this.transportId = transportId;
28 | this.configuration = configuration;
29 | }
30 |
31 | public void register(boolean renew) throws Exception {
32 | setRegistration(renew);
33 | }
34 |
35 | public PjSipService getService() {
36 | return service;
37 | }
38 |
39 | public int getTransportId() {
40 | return transportId;
41 | }
42 |
43 | public AccountConfigurationDTO getConfiguration() {
44 | return configuration;
45 | }
46 |
47 | public String getRegistrationStatusText() {
48 | try {
49 | return getInfo().getRegStatusText();
50 | } catch (Exception e) {
51 | return "Connecting...";
52 | }
53 | }
54 |
55 | @Override
56 | public void onRegState(OnRegStateParam prm) {
57 | reason = prm.getReason();
58 | service.emmitRegistrationChanged(this, prm);
59 | }
60 |
61 | @Override
62 | public void onIncomingCall(OnIncomingCallParam prm) {
63 | PjSipCall call = new PjSipCall(this, prm.getCallId());
64 | service.emmitCallReceived(this, call);
65 | }
66 |
67 | @Override
68 | public void onInstantMessage(OnInstantMessageParam prm) {
69 | PjSipMessage message = new PjSipMessage(this, prm);
70 | service.emmitMessageReceived(this, message);
71 | }
72 |
73 | public JSONObject toJson() {
74 | JSONObject json = new JSONObject();
75 |
76 | try {
77 | JSONObject registration = new JSONObject();
78 | registration.put("status", getInfo().getRegStatus());
79 | registration.put("statusText", getInfo().getRegStatusText());
80 | registration.put("active", getInfo().getRegIsActive());
81 | registration.put("reason", reason);
82 |
83 | json.put("id", getId());
84 | json.put("uri", getInfo().getUri());
85 | json.put("name", configuration.getName());
86 | json.put("username", configuration.getUsername());
87 | json.put("domain", configuration.getDomain());
88 | json.put("password", configuration.getPassword());
89 | json.put("proxy", configuration.getProxy());
90 | json.put("transport", configuration.getTransport());
91 |
92 | json.put("contactParams", configuration.getContactParams());
93 | json.put("contactUriParams", configuration.getContactUriParams());
94 |
95 | json.put("regServer", configuration.getRegServer());
96 | json.put("regTimeout", configuration.isRegTimeoutNotEmpty() ? String.valueOf(configuration.getRegTimeout()) : "");
97 | json.put("regContactParams", configuration.getRegContactParams());
98 | json.put("regHeaders", configuration.getRegHeaders());
99 | json.put("regOnAdd", configuration.isRegOnAdd());
100 |
101 | json.put("registration", registration);
102 |
103 | return json;
104 | } catch (Exception e) {
105 | throw new RuntimeException(e);
106 | }
107 | }
108 |
109 | public String toJsonString() {
110 | return toJson().toString();
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipBroadcastEmiter.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.util.Log;
6 | import org.json.JSONArray;
7 | import org.json.JSONObject;
8 |
9 | import java.util.List;
10 |
11 | import com.carusto.ReactNativePjSip.dto.AccountConfigurationDTO;
12 |
13 | public class PjSipBroadcastEmiter {
14 |
15 | private static String TAG = "PjSipBroadcastEmiter";
16 |
17 | private Context context;
18 |
19 | public PjSipBroadcastEmiter(Context context) {
20 | this.context = context;
21 | }
22 |
23 | public void fireStarted(Intent original, List accounts, List calls, JSONObject settings) {
24 | try {
25 | JSONArray dataAccounts = new JSONArray();
26 | for (PjSipAccount account : accounts) {
27 | dataAccounts.put(account.toJson());
28 | }
29 |
30 | JSONArray dataCalls = new JSONArray();
31 | for (PjSipCall call : calls) {
32 | dataCalls.put(call.toJson());
33 | }
34 |
35 | JSONObject data = new JSONObject();
36 | data.put("accounts", dataAccounts);
37 | data.put("calls", dataCalls);
38 | data.put("settings", settings);
39 |
40 | Intent intent = new Intent();
41 | intent.setAction(PjActions.EVENT_STARTED);
42 | intent.putExtra("callback_id", original.getIntExtra("callback_id", -1));
43 | intent.putExtra("data", data.toString());
44 |
45 | context.sendBroadcast(intent);
46 | } catch (Exception e) {
47 | Log.e(TAG, "Failed to send ACCOUNT_CREATED event", e);
48 | }
49 | }
50 |
51 | public void fireStopped(Intent original) {
52 | Intent intent = new Intent();
53 | intent.setAction(PjActions.EVENT_STOPPED);
54 | intent.putExtra("callback_id", original.getIntExtra("callback_id", -1));
55 |
56 | context.sendBroadcast(intent);
57 | }
58 |
59 | public void fireIntentHandled(Intent original, JSONObject result) {
60 | Intent intent = new Intent();
61 | intent.setAction(PjActions.EVENT_HANDLED);
62 | intent.putExtra("callback_id", original.getIntExtra("callback_id", -1));
63 | intent.putExtra("data", result.toString());
64 |
65 | context.sendBroadcast(intent);
66 | }
67 |
68 | public void fireIntentHandled(Intent original) {
69 | Intent intent = new Intent();
70 | intent.setAction(PjActions.EVENT_HANDLED);
71 | intent.putExtra("callback_id", original.getIntExtra("callback_id", -1));
72 |
73 | context.sendBroadcast(intent);
74 | }
75 |
76 | public void fireIntentHandled(Intent original, Exception e) {
77 | Intent intent = new Intent();
78 | intent.setAction(PjActions.EVENT_HANDLED);
79 | intent.putExtra("callback_id", original.getIntExtra("callback_id", -1));
80 | intent.putExtra("exception", e.getMessage());
81 |
82 | context.sendBroadcast(intent);
83 | }
84 |
85 | public void fireAccountCreated(Intent original, PjSipAccount account) {
86 | Intent intent = new Intent();
87 | intent.setAction(PjActions.EVENT_ACCOUNT_CREATED);
88 | intent.putExtra("callback_id", original.getIntExtra("callback_id", -1));
89 | intent.putExtra("data", account.toJsonString());
90 |
91 | context.sendBroadcast(intent);
92 | }
93 |
94 | public void fireAccountRetrieved(Intent original, PjSipAccount account) {
95 | Intent intent = new Intent();
96 | intent.setAction(PjActions.EVENT_ACCOUNT_RETRIEVED);
97 | intent.putExtra("callback_id", original.getIntExtra("callback_id", -1));
98 | intent.putExtra("data", account.toJsonString());
99 |
100 | context.sendBroadcast(intent);
101 | }
102 |
103 | public void fireAccountsRetrieved(Intent original, List> accounts) {
104 | try {
105 | JSONArray dataAccounts = new JSONArray();
106 | for (Object account : accounts) {
107 | if (account instanceof PjSipAccount) {
108 | PjSipAccount acc = (PjSipAccount) account;
109 | dataAccounts.put(acc.toJson());
110 | } else if (account instanceof AccountConfigurationDTO) {
111 | AccountConfigurationDTO cfg = (AccountConfigurationDTO) account;
112 | dataAccounts.put(cfg.toJson());
113 | }
114 | }
115 |
116 | Intent intent = new Intent();
117 | intent.setAction(PjActions.EVENT_ACCOUNTS_RETRIEVED);
118 | intent.putExtra("callback_id", original.getIntExtra("callback_id", -1));
119 | intent.putExtra("data", dataAccounts.toString());
120 |
121 | context.sendBroadcast(intent);
122 | } catch (Exception e) {
123 | Log.e(TAG, "Failed to send ACCOUNTS_RETRIEVED event", e);
124 | }
125 | }
126 |
127 | public void fireRegistrationChangeEvent(PjSipAccount account) {
128 | Intent intent = new Intent();
129 | intent.setAction(PjActions.EVENT_REGISTRATION_CHANGED);
130 | intent.putExtra("data", account.toJsonString());
131 |
132 | context.sendBroadcast(intent);
133 | }
134 |
135 | public void fireMessageReceivedEvent(PjSipMessage message) {
136 | Intent intent = new Intent();
137 | intent.setAction(PjActions.EVENT_MESSAGE_RECEIVED);
138 | intent.putExtra("data", message.toJsonString());
139 |
140 | context.sendBroadcast(intent);
141 | }
142 |
143 | public void fireCallReceivedEvent(PjSipCall call) {
144 | Intent intent = new Intent();
145 | intent.setAction(PjActions.EVENT_CALL_RECEIVED);
146 | intent.putExtra("data", call.toJsonString());
147 |
148 | context.sendBroadcast(intent);
149 | }
150 |
151 | public void fireCallChanged(PjSipCall call) {
152 | Intent intent = new Intent();
153 | intent.setAction(PjActions.EVENT_CALL_CHANGED);
154 | intent.putExtra("data", call.toJsonString());
155 |
156 | context.sendBroadcast(intent);
157 | }
158 |
159 | public void fireCallTerminated(PjSipCall call) {
160 | Intent intent = new Intent();
161 | intent.setAction(PjActions.EVENT_CALL_TERMINATED);
162 | intent.putExtra("data", call.toJsonString());
163 |
164 | context.sendBroadcast(intent);
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipBroadcastReceiver.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.IntentFilter;
7 | import android.util.Log;
8 | import com.carusto.ReactNativePjSip.utils.ArgumentUtils;
9 | import com.facebook.react.bridge.Callback;
10 | import com.facebook.react.bridge.ReactApplicationContext;
11 | import com.facebook.react.modules.core.DeviceEventManagerModule;
12 |
13 | import java.util.HashMap;
14 |
15 | import javax.annotation.Nullable;
16 |
17 | public class PjSipBroadcastReceiver extends BroadcastReceiver {
18 |
19 | private static String TAG = "PjSipBroadcastReceiver";
20 |
21 | private int seq = 0;
22 |
23 | private ReactApplicationContext context;
24 |
25 | private HashMap callbacks = new HashMap<>();
26 |
27 | public PjSipBroadcastReceiver(ReactApplicationContext context) {
28 | this.context = context;
29 | }
30 |
31 | public void setContext(ReactApplicationContext context) {
32 | this.context = context;
33 | }
34 |
35 | public int register(Callback callback) {
36 | int id = ++seq;
37 | callbacks.put(id, callback);
38 | return id;
39 | }
40 |
41 | public IntentFilter getFilter() {
42 | IntentFilter filter = new IntentFilter();
43 | filter.addAction(PjActions.EVENT_STARTED);
44 | filter.addAction(PjActions.EVENT_ACCOUNT_CREATED);
45 | filter.addAction(PjActions.EVENT_ACCOUNT_RETRIEVED);
46 | filter.addAction(PjActions.EVENT_ACCOUNTS_RETRIEVED);
47 | filter.addAction(PjActions.EVENT_REGISTRATION_CHANGED);
48 | filter.addAction(PjActions.EVENT_CALL_RECEIVED);
49 | filter.addAction(PjActions.EVENT_CALL_CHANGED);
50 | filter.addAction(PjActions.EVENT_CALL_TERMINATED);
51 | filter.addAction(PjActions.EVENT_CALL_SCREEN_LOCKED);
52 | filter.addAction(PjActions.EVENT_MESSAGE_RECEIVED);
53 | filter.addAction(PjActions.EVENT_HANDLED);
54 |
55 | return filter;
56 | }
57 |
58 | @Override
59 | public void onReceive(Context context, Intent intent) {
60 | String action = intent.getAction();
61 |
62 | Log.d(TAG, "Received \""+ action +"\" response from service (" + ArgumentUtils.dumpIntentExtraParameters(intent) + ")");
63 |
64 | switch (action) {
65 | case PjActions.EVENT_STARTED:
66 | onCallback(intent);
67 | break;
68 | case PjActions.EVENT_STOPPED:
69 | onCallback(intent);
70 | break;
71 | case PjActions.EVENT_ACCOUNT_CREATED:
72 | onCallback(intent);
73 | break;
74 | case PjActions.EVENT_ACCOUNT_RETRIEVED:
75 | onCallback(intent);
76 | break;
77 | case PjActions.EVENT_ACCOUNTS_RETRIEVED:
78 | onCallback(intent);
79 | break;
80 | case PjActions.EVENT_REGISTRATION_CHANGED:
81 | onRegistrationChanged(intent);
82 | break;
83 | case PjActions.EVENT_MESSAGE_RECEIVED:
84 | onMessageReceived(intent);
85 | break;
86 | case PjActions.EVENT_CALL_RECEIVED:
87 | onCallReceived(intent);
88 | break;
89 | case PjActions.EVENT_CALL_CHANGED:
90 | onCallChanged(intent);
91 | break;
92 | case PjActions.EVENT_CALL_TERMINATED:
93 | onCallTerminated(intent);
94 | break;
95 | default:
96 | onCallback(intent);
97 | break;
98 | }
99 | }
100 |
101 | private void onRegistrationChanged(Intent intent) {
102 | String json = intent.getStringExtra("data");
103 | Object params = ArgumentUtils.fromJson(json);
104 | emit("pjSipRegistrationChanged", params);
105 | }
106 |
107 | private void onMessageReceived(Intent intent) {
108 | String json = intent.getStringExtra("data");
109 | Object params = ArgumentUtils.fromJson(json);
110 |
111 | emit("pjSipMessageReceived", params);
112 | }
113 |
114 | private void onCallReceived(Intent intent) {
115 | String json = intent.getStringExtra("data");
116 | Object params = ArgumentUtils.fromJson(json);
117 | emit("pjSipCallReceived", params);
118 | }
119 |
120 | private void onCallChanged(Intent intent) {
121 | String json = intent.getStringExtra("data");
122 | Object params = ArgumentUtils.fromJson(json);
123 | emit("pjSipCallChanged", params);
124 | }
125 |
126 | private void onCallTerminated(Intent intent) {
127 | String json = intent.getStringExtra("data");
128 | Object params = ArgumentUtils.fromJson(json);
129 | emit("pjSipCallTerminated", params);
130 | }
131 |
132 | private void onCallback(Intent intent) {
133 | // Define callback
134 | Callback callback = null;
135 |
136 | if (intent.hasExtra("callback_id")) {
137 | int id = intent.getIntExtra("callback_id", -1);
138 | if (callbacks.containsKey(id)) {
139 | callback = callbacks.remove(id);
140 | } else {
141 | Log.w(TAG, "Callback with \""+ id +"\" identifier not found (\""+ intent.getAction() +"\")");
142 | }
143 | }
144 |
145 | if (callback == null) {
146 | return;
147 | }
148 |
149 | // -----
150 | if (intent.hasExtra("exception")) {
151 | Log.w(TAG, "Callback executed with exception state: " + intent.getStringExtra("exception"));
152 | callback.invoke(false, intent.getStringExtra("exception"));
153 | } else if (intent.hasExtra("data")) {
154 | Object params = ArgumentUtils.fromJson(intent.getStringExtra("data"));
155 | callback.invoke(true, params);
156 | } else {
157 | callback.invoke(true, true);
158 | }
159 | }
160 |
161 | private void emit(String eventName, @Nullable Object data) {
162 | Log.d(TAG, "emit " + eventName + " / " + data);
163 |
164 | context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, data);
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipLogWriter.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | import android.util.Log;
4 | import org.pjsip.pjsua2.LogEntry;
5 | import org.pjsip.pjsua2.LogWriter;
6 | import org.pjsip.pjsua2.pjsua2JNI;
7 |
8 | public class PjSipLogWriter extends LogWriter {
9 |
10 | private static String TAG = "PjSipLogWriter";
11 |
12 | public void write(LogEntry entry) {
13 | Log.d(TAG, entry.getMsg());
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipMessage.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | import org.json.JSONObject;
4 | import org.pjsip.pjsua2.OnInstantMessageParam;
5 |
6 | public class PjSipMessage {
7 |
8 | private PjSipAccount account;
9 |
10 | private OnInstantMessageParam prm;
11 |
12 | public PjSipMessage(PjSipAccount account, OnInstantMessageParam prm) {
13 | this.account = account;
14 | this.prm = prm;
15 | }
16 |
17 | public OnInstantMessageParam getParam() {
18 | return prm;
19 | }
20 |
21 | public JSONObject toJson() {
22 | JSONObject json = new JSONObject();
23 |
24 | try {
25 | // -----
26 | json.put("accountId", account.getId());
27 |
28 | // -----
29 | json.put("contactUri", prm.getContactUri());
30 | json.put("fromUri", prm.getFromUri());
31 | json.put("toUri", prm.getToUri());
32 | json.put("body", prm.getMsgBody());
33 | json.put("contentType", prm.getContentType());
34 |
35 | return json;
36 | } catch (Exception e) {
37 | throw new RuntimeException(e);
38 | }
39 | }
40 |
41 | public String toJsonString() {
42 | return toJson().toString();
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipModule.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 |
6 | import com.facebook.react.bridge.*;
7 |
8 | public class PjSipModule extends ReactContextBaseJavaModule {
9 |
10 | private static PjSipBroadcastReceiver receiver;
11 |
12 | public PjSipModule(ReactApplicationContext context) {
13 | super(context);
14 |
15 | // Module could be started several times, but we have to register receiver only once.
16 | if (receiver == null) {
17 | receiver = new PjSipBroadcastReceiver(context);
18 | this.getReactApplicationContext().registerReceiver(receiver, receiver.getFilter());
19 | } else {
20 | receiver.setContext(context);
21 | }
22 | }
23 |
24 | @Override
25 | public String getName() {
26 | return "PjSipModule";
27 | }
28 |
29 | @ReactMethod
30 | public void start(ReadableMap configuration, Callback callback) {
31 | int id = receiver.register(callback);
32 | Intent intent = PjActions.createStartIntent(id, configuration, getReactApplicationContext());
33 |
34 | getReactApplicationContext().startService(intent);
35 | }
36 |
37 | @ReactMethod
38 | public void stop(Callback callback) {
39 | int id = receiver.register(callback);
40 | Intent intent = PjActions.createStopIntent(id, getReactApplicationContext());
41 |
42 | getReactApplicationContext().stopService(intent);
43 | }
44 |
45 | @ReactMethod
46 | public void changeServiceConfiguration(ReadableMap configuration, Callback callback) {
47 | int id = receiver.register(callback);
48 | Intent intent = PjActions.createSetServiceConfigurationIntent(id, configuration, getReactApplicationContext());
49 | getReactApplicationContext().startService(intent);
50 | }
51 |
52 | @ReactMethod
53 | public void createAccount(ReadableMap configuration, Callback callback) {
54 | int id = receiver.register(callback);
55 | Intent intent = PjActions.createAccountCreateIntent(id, configuration, getReactApplicationContext());
56 | getReactApplicationContext().startService(intent);
57 | }
58 |
59 | @ReactMethod
60 | public void registerAccount(int accountId, boolean renew, Callback callback) {
61 | int id = receiver.register(callback);
62 | Intent intent = PjActions.createAccountRegisterIntent(id, accountId, renew, getReactApplicationContext());
63 | getReactApplicationContext().startService(intent);
64 | }
65 |
66 | @ReactMethod
67 | public void deleteAccount(int accountId, Callback callback) {
68 | int callbackId = receiver.register(callback);
69 | Intent intent = PjActions.createAccountDeleteIntent(callbackId, accountId, getReactApplicationContext());
70 | getReactApplicationContext().startService(intent);
71 | }
72 |
73 | @ReactMethod
74 | public void getAccount(int accountId, Callback callback) {
75 | int callbackId = receiver.register(callback);
76 | Intent intent = PjActions.createGetAccountIntent(callbackId, accountId, getReactApplicationContext());
77 | getReactApplicationContext().startService(intent);
78 | }
79 |
80 | @ReactMethod
81 | public void getAccounts(Callback callback) {
82 | int callbackId = receiver.register(callback);
83 | Intent intent = PjActions.createGetAccountsIntent(callbackId, getReactApplicationContext());
84 | getReactApplicationContext().startService(intent);
85 | }
86 |
87 | @ReactMethod
88 | public void makeCall(int accountId, String destination, ReadableMap callSettings, ReadableMap msgData, Callback callback) {
89 | int callbackId = receiver.register(callback);
90 | Intent intent = PjActions.createMakeCallIntent(callbackId, accountId, destination, callSettings, msgData, getReactApplicationContext());
91 | getReactApplicationContext().startService(intent);
92 | }
93 |
94 | @ReactMethod
95 | public void hangupCall(int callId, Callback callback) {
96 | int callbackId = receiver.register(callback);
97 | Intent intent = PjActions.createHangupCallIntent(callbackId, callId, getReactApplicationContext());
98 | getReactApplicationContext().startService(intent);
99 | }
100 |
101 | @ReactMethod
102 | public void declineCall(int callId, Callback callback) {
103 | int callbackId = receiver.register(callback);
104 | Intent intent = PjActions.createDeclineCallIntent(callbackId, callId, getReactApplicationContext());
105 | getReactApplicationContext().startService(intent);
106 | }
107 |
108 | @ReactMethod
109 | public void answerCall(int callId, Callback callback) {
110 | int callbackId = receiver.register(callback);
111 | Intent intent = PjActions.createAnswerCallIntent(callbackId, callId, getReactApplicationContext());
112 | getReactApplicationContext().startService(intent);
113 | }
114 |
115 | @ReactMethod
116 | public void holdCall(int callId, Callback callback) {
117 | int callbackId = receiver.register(callback);
118 | Intent intent = PjActions.createHoldCallIntent(callbackId, callId, getReactApplicationContext());
119 | getReactApplicationContext().startService(intent);
120 | }
121 |
122 | @ReactMethod
123 | public void unholdCall(int callId, Callback callback) {
124 | int callbackId = receiver.register(callback);
125 | Intent intent = PjActions.createUnholdCallIntent(callbackId, callId, getReactApplicationContext());
126 | getReactApplicationContext().startService(intent);
127 | }
128 |
129 | @ReactMethod
130 | public void muteCall(int callId, Callback callback) {
131 | int callbackId = receiver.register(callback);
132 | Intent intent = PjActions.createMuteCallIntent(callbackId, callId, getReactApplicationContext());
133 | getReactApplicationContext().startService(intent);
134 | }
135 |
136 | @ReactMethod
137 | public void unMuteCall(int callId, Callback callback) {
138 | int callbackId = receiver.register(callback);
139 | Intent intent = PjActions.createUnMuteCallIntent(callbackId, callId, getReactApplicationContext());
140 | getReactApplicationContext().startService(intent);
141 | }
142 |
143 | @ReactMethod
144 | public void useSpeaker(Callback callback) {
145 | int callbackId = receiver.register(callback);
146 | Intent intent = PjActions.createUseSpeakerCallIntent(callbackId, getReactApplicationContext());
147 | getReactApplicationContext().startService(intent);
148 | }
149 |
150 | @ReactMethod
151 | public void useEarpiece(Callback callback) {
152 | int callbackId = receiver.register(callback);
153 | Intent intent = PjActions.createUseEarpieceCallIntent(callbackId, getReactApplicationContext());
154 | getReactApplicationContext().startService(intent);
155 | }
156 |
157 | @ReactMethod
158 | public void xferCall(int callId, String destination, Callback callback) {
159 | int callbackId = receiver.register(callback);
160 | Intent intent = PjActions.createXFerCallIntent(callbackId, callId, destination, getReactApplicationContext());
161 | getReactApplicationContext().startService(intent);
162 | }
163 |
164 | @ReactMethod
165 | public void xferReplacesCall(int callId, int destCallId, Callback callback) {
166 | int callbackId = receiver.register(callback);
167 | Intent intent = PjActions.createXFerReplacesCallIntent(callbackId, callId, destCallId, getReactApplicationContext());
168 | getReactApplicationContext().startService(intent);
169 | }
170 |
171 | @ReactMethod
172 | public void redirectCall(int callId, String destination, Callback callback) {
173 | int callbackId = receiver.register(callback);
174 | Intent intent = PjActions.createRedirectCallIntent(callbackId, callId, destination, getReactApplicationContext());
175 | getReactApplicationContext().startService(intent);
176 | }
177 |
178 | @ReactMethod
179 | public void dtmfCall(int callId, String digits, Callback callback) {
180 | int callbackId = receiver.register(callback);
181 | Intent intent = PjActions.createDtmfCallIntent(callbackId, callId, digits, getReactApplicationContext());
182 | getReactApplicationContext().startService(intent);
183 | }
184 |
185 | @ReactMethod
186 | public void changeCodecSettings(ReadableMap codecSettings, Callback callback) {
187 | int callbackId = receiver.register(callback);
188 | Intent intent = PjActions.createChangeCodecSettingsIntent(callbackId, codecSettings, getReactApplicationContext());
189 | getReactApplicationContext().startService(intent);
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipModulePackage.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.NativeModule;
5 | import com.facebook.react.bridge.ReactApplicationContext;
6 | import com.facebook.react.uimanager.ViewManager;
7 |
8 | import java.util.ArrayList;
9 | import java.util.Arrays;
10 | import java.util.List;
11 |
12 | public class PjSipModulePackage implements ReactPackage {
13 |
14 | public PjSipModulePackage() {
15 |
16 | }
17 |
18 | @Override
19 | public List createNativeModules(
20 | ReactApplicationContext reactContext) {
21 | List modules = new ArrayList<>();
22 |
23 | modules.add(new PjSipModule(reactContext));
24 | return modules;
25 | }
26 |
27 | @Override
28 | public List createViewManagers(ReactApplicationContext reactContext) {
29 | return Arrays.asList(
30 | new PjSipRemoteVideoViewManager(),
31 | new PjSipPreviewVideoViewManager()
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipPreviewVideo.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 | import android.view.SurfaceHolder;
6 |
7 | import org.pjsip.pjsua2.VideoPreview;
8 | import org.pjsip.pjsua2.VideoPreviewOpParam;
9 | import org.pjsip.pjsua2.VideoWindow;
10 | import org.pjsip.pjsua2.VideoWindowHandle;
11 |
12 | public class PjSipPreviewVideo extends PjSipVideo {
13 |
14 | private int deviceId = -1;
15 |
16 | public PjSipPreviewVideo(Context context) {
17 | super(context);
18 | }
19 |
20 | public void setDeviceId(int deviceId) {
21 | if (this.deviceId == deviceId) {
22 | return;
23 | }
24 |
25 | final VideoPreview windowPreview = new VideoPreview(deviceId);
26 |
27 | setCallback(new PjSipVideoWindowHandler() {
28 | @Override
29 | public VideoWindow start(SurfaceHolder surfaceHolder) throws Exception {
30 | VideoWindowHandle handle = new VideoWindowHandle();
31 | handle.getHandle().setWindow(surfaceHolder.getSurface());
32 |
33 | VideoPreviewOpParam op = new VideoPreviewOpParam();
34 | op.setWindow(handle);
35 |
36 | windowPreview.start(op);
37 | return windowPreview.getVideoWindow();
38 | }
39 | });
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipPreviewVideoViewManager.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | import android.graphics.Color;
4 | import android.util.Log;
5 | import android.view.SurfaceView;
6 | import android.view.View;
7 | import android.widget.RelativeLayout;
8 | import android.widget.TextView;
9 |
10 | import com.facebook.react.uimanager.annotations.ReactProp;
11 | import com.facebook.react.uimanager.SimpleViewManager;
12 | import com.facebook.react.uimanager.ThemedReactContext;
13 | import com.facebook.react.uimanager.ViewProps;
14 |
15 | import org.pjsip.pjsua2.MediaFormat;
16 | import org.pjsip.pjsua2.VideoDevInfo;
17 | import org.pjsip.pjsua2.VideoPreview;
18 | import org.pjsip.pjsua2.VideoPreviewOpParam;
19 | import org.pjsip.pjsua2.VideoWindowHandle;
20 | import org.pjsip.pjsua2.WindowHandle;
21 |
22 | public class PjSipPreviewVideoViewManager extends SimpleViewManager {
23 |
24 | private String LOCAL_VIDEO_CLASS = "PjSipPreviewVideoView";
25 |
26 | @Override
27 | public String getName() {
28 | return LOCAL_VIDEO_CLASS;
29 | }
30 |
31 | @ReactProp(name = "deviceId")
32 | public void setDeviceId(PjSipPreviewVideo view, int deviceId) {
33 | view.setDeviceId(deviceId);
34 | }
35 |
36 | @ReactProp(name = "objectFit")
37 | public void setObjectFit(PjSipPreviewVideo view, String objectFit) {
38 | view.setObjectFit(objectFit);
39 | }
40 |
41 | @Override
42 | protected PjSipPreviewVideo createViewInstance(ThemedReactContext reactContext) {
43 | return new PjSipPreviewVideo(reactContext);
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipRemoteVideo.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | import android.content.Context;
4 | import android.os.Handler;
5 | import android.util.Log;
6 | import android.view.SurfaceHolder;
7 |
8 | import org.pjsip.pjsua2.VideoWindow;
9 | import org.pjsip.pjsua2.VideoWindowHandle;
10 | import org.pjsip.pjsua2.WindowHandle;
11 |
12 | public class PjSipRemoteVideo extends PjSipVideo implements PjSipVideoMediaChange {
13 |
14 | private static String TAG = "PjSipRemoteVideo";
15 |
16 | public PjSipRemoteVideo(Context context) {
17 | super(context);
18 | }
19 |
20 | @Override
21 | protected void onAttachedToWindow() {
22 | super.onAttachedToWindow();
23 |
24 | PjSipCall.mediaListeners.add(this);
25 | }
26 |
27 | @Override
28 | protected void onDetachedFromWindow() {
29 | super.onDetachedFromWindow();
30 |
31 | PjSipCall.mediaListeners.remove(this);
32 | }
33 |
34 | public void setWindowId(int windowId) {
35 | final VideoWindow videoWindow = new VideoWindow(windowId);
36 |
37 | setCallback(new PjSipVideoWindowHandler() {
38 | @Override
39 | public VideoWindow start(SurfaceHolder surfaceHolder) throws Exception {
40 | WindowHandle winHandle = new WindowHandle();
41 | winHandle.setWindow(surfaceHolder.getSurface());
42 |
43 | VideoWindowHandle handle = new VideoWindowHandle();
44 | handle.setHandle(winHandle);
45 |
46 | videoWindow.setWindow(handle);
47 | return videoWindow;
48 | }
49 | });
50 | }
51 |
52 | @Override
53 | public void onChange() {
54 | Handler mainHandler = new Handler(getContext().getMainLooper());
55 | mainHandler.post(new Runnable() {
56 | @Override
57 | public void run() {
58 | try {
59 | doLayout();
60 | } catch (Exception e) {
61 | Log.e(TAG, "An error occurs while layout a video", e);
62 | }
63 | }
64 | });
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipRemoteVideoViewManager.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | import android.graphics.Color;
4 | import android.util.Log;
5 | import android.view.SurfaceView;
6 | import android.view.View;
7 | import android.widget.RelativeLayout;
8 | import android.widget.TextView;
9 |
10 | import com.facebook.react.uimanager.annotations.ReactProp;
11 | import com.facebook.react.uimanager.SimpleViewManager;
12 | import com.facebook.react.uimanager.ThemedReactContext;
13 | import com.facebook.react.uimanager.ViewProps;
14 |
15 | import org.pjsip.pjsua2.MediaFormat;
16 | import org.pjsip.pjsua2.VideoDevInfo;
17 | import org.pjsip.pjsua2.VideoPreview;
18 | import org.pjsip.pjsua2.VideoPreviewOpParam;
19 | import org.pjsip.pjsua2.VideoWindow;
20 | import org.pjsip.pjsua2.VideoWindowHandle;
21 | import org.pjsip.pjsua2.WindowHandle;
22 |
23 | public class PjSipRemoteVideoViewManager extends SimpleViewManager {
24 |
25 | private String LOCAL_VIDEO_CLASS = "PjSipRemoteVideoView";
26 |
27 | @Override
28 | public String getName() {
29 | return LOCAL_VIDEO_CLASS;
30 | }
31 |
32 | @ReactProp(name = "windowId")
33 | public void setWindowId(PjSipRemoteVideo view, int windowId) {
34 | view.setWindowId(windowId);
35 | }
36 |
37 | @ReactProp(name = "objectFit")
38 | public void setObjectFit(PjSipRemoteVideo view, String objectFit) {
39 | view.setObjectFit(objectFit);
40 | }
41 |
42 | @Override
43 | protected PjSipRemoteVideo createViewInstance(ThemedReactContext reactContext) {
44 | return new PjSipRemoteVideo(reactContext);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipUtils.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | import org.pjsip.pjsua2.SipHeader;
4 | import org.pjsip.pjsua2.SipHeaderVector;
5 |
6 | import java.util.Map;
7 |
8 | public class PjSipUtils {
9 |
10 | public static SipHeaderVector mapToSipHeaderVector(Map headers) {
11 | SipHeaderVector hdrsVector = new SipHeaderVector();
12 |
13 | for (Map.Entry entry : headers.entrySet()) {
14 | SipHeader hdr = new SipHeader();
15 | hdr.setHName(entry.getKey());
16 | hdr.setHValue(entry.getValue());
17 |
18 | hdrsVector.add(hdr);
19 | }
20 |
21 | return hdrsVector;
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipVideo.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 | import android.view.SurfaceHolder;
6 | import android.view.SurfaceView;
7 | import android.view.ViewGroup;
8 |
9 | import org.pjsip.pjsua2.MediaSize;
10 | import org.pjsip.pjsua2.VideoWindow;
11 |
12 | public abstract class PjSipVideo extends ViewGroup implements SurfaceHolder.Callback {
13 |
14 | private static String TAG = "PjSipVideo";
15 |
16 | private String objectFit = "cover";
17 |
18 | private SurfaceHolder surfaceHolder;
19 |
20 | private SurfaceView surfaceView;
21 |
22 | private PjSipVideoWindowHandler videoWindowHandler;
23 |
24 | private VideoWindow videoWindow;
25 |
26 | private int layoutLeft = 0;
27 |
28 | private int layoutTop = 0;
29 |
30 | private int layoutRight = 0;
31 |
32 | private int layoutBottom = 0;
33 |
34 | public PjSipVideo(Context context) {
35 | super(context);
36 |
37 | surfaceView = new SurfaceView(context);
38 | surfaceView.getHolder().addCallback(this);
39 | surfaceView.setZOrderOnTop(false);
40 | addView(surfaceView);
41 | }
42 |
43 | protected void setCallback(PjSipVideoWindowHandler callback) {
44 | videoWindowHandler = callback;
45 |
46 | if (surfaceHolder != null) {
47 | try {
48 | videoWindow = this.videoWindowHandler.start(surfaceHolder);
49 | doLayout();
50 | } catch (Exception e) {
51 | Log.e(TAG, "An error occurs during getting video window by surface", e);
52 | }
53 | }
54 | }
55 |
56 | public void setObjectFit(String type) {
57 | if (objectFit.equals(type.toLowerCase())) {
58 | return;
59 | }
60 |
61 | objectFit = type.toLowerCase();
62 |
63 | try {
64 | doLayout();
65 | } catch (Exception e) {
66 | Log.e(TAG, "An error occurs during setting object fit with active video window.", e);
67 | }
68 | }
69 |
70 | @Override
71 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
72 | layoutTop = t;
73 | layoutLeft = l;
74 | layoutBottom = b;
75 | layoutRight = r;
76 |
77 | if (surfaceHolder == null) {
78 | surfaceView.layout(0, 0, r - l, b - t);
79 | } else {
80 | try {
81 | doLayout();
82 | } catch (Exception e) {
83 | Log.e(TAG, "An error occurs during layout", e);
84 | }
85 | }
86 | }
87 |
88 | protected void doLayout() throws Exception {
89 | int layoutWidth = layoutRight - layoutLeft;
90 | int layoutHeight = layoutBottom - layoutTop;
91 |
92 | if (surfaceView == null || videoWindow == null) {
93 | return;
94 | }
95 |
96 | MediaSize size = videoWindow.getInfo().getSize();
97 | int height;
98 | int width;
99 |
100 | switch (objectFit) {
101 | case "cover": {
102 | if (size.getH() == size.getW()) {
103 | int max = Math.max(layoutWidth, layoutHeight);
104 | width = max;
105 | height = max;
106 | } else if (size.getW() / layoutWidth > size.getH() / layoutHeight) {
107 | height = layoutHeight;
108 | width = Math.round(((float) layoutHeight / size.getH()) * size.getW());
109 | } else {
110 | width = layoutWidth;
111 | height = Math.round(((float) layoutWidth / size.getW()) * size.getH());
112 | }
113 |
114 | break;
115 | }
116 | case "contain":
117 | default: {
118 | if (size.getH() == size.getW()) {
119 | int min = Math.min(layoutWidth, layoutHeight);
120 | width = min;
121 | height = min;
122 | } else if (size.getW() > size.getH()) {
123 | width = layoutWidth;
124 | height = Math.round(((float) layoutWidth / size.getW()) * size.getH());
125 | } else {
126 | height = layoutHeight;
127 | width = Math.round(((float) layoutHeight / size.getH()) * size.getW());
128 | }
129 | }
130 | }
131 |
132 | int offsetLeft = (layoutWidth - width) / 2;
133 | int offsetTop = (layoutHeight - height) / 2;
134 |
135 | Log.d(TAG, "resize to video width-" + size.getW());
136 | Log.d(TAG, "resize to video height-" + size.getH());
137 | Log.d(TAG, "resize to width-" + width);
138 | Log.d(TAG, "resize to height-" + height);
139 | Log.d(TAG, "resize to layoutWidth-" + layoutWidth);
140 | Log.d(TAG, "resize to layoutHeight-" + layoutHeight);
141 | Log.d(TAG, "resize to offsetLeft-" + offsetLeft);
142 | Log.d(TAG, "resize to offsetTop-" + offsetTop);
143 |
144 | surfaceView.layout(
145 | offsetLeft,
146 | offsetTop,
147 | offsetLeft + width,
148 | offsetTop + height
149 | );
150 | }
151 |
152 | @Override
153 | public void surfaceCreated(final SurfaceHolder holder) {
154 | this.surfaceHolder = holder;
155 |
156 | if (this.videoWindowHandler != null) {
157 | try {
158 | videoWindow = this.videoWindowHandler.start(holder);
159 | doLayout();
160 | } catch (Exception e) {
161 | Log.e(TAG, "An error occurs during getting video window by surface", e);
162 | }
163 | }
164 | }
165 |
166 | @Override
167 | public void surfaceChanged(final SurfaceHolder surfaceHolder, int i, int i1, int i2) {
168 | // Nothing
169 | }
170 |
171 | @Override
172 | public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
173 | // Nothing
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipVideoMediaChange.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | public interface PjSipVideoMediaChange {
4 | void onChange();
5 | }
6 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/PjSipVideoWindowHandler.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip;
2 |
3 | import android.view.SurfaceHolder;
4 |
5 | import org.pjsip.pjsua2.VideoWindow;
6 |
7 | public interface PjSipVideoWindowHandler {
8 | VideoWindow start(SurfaceHolder surfaceHolder) throws Exception;
9 | }
10 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/dto/AccountConfigurationDTO.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip.dto;
2 |
3 | import android.content.Intent;
4 | import androidx.annotation.Nullable;
5 |
6 | import java.util.Map;
7 | import org.json.JSONObject;
8 |
9 | public class AccountConfigurationDTO {
10 |
11 | private Integer id;
12 |
13 | public String name;
14 |
15 | public String username;
16 |
17 | public String domain;
18 |
19 | public String password;
20 |
21 | public String proxy;
22 |
23 | public String transport;
24 |
25 | private String contactParams;
26 |
27 | private String contactUriParams;
28 |
29 | public String regServer;
30 |
31 | @Nullable
32 | public Integer regTimeout;
33 |
34 | public Map regHeaders;
35 |
36 | public String regContactParams;
37 |
38 | public boolean regOnAdd;
39 |
40 | public Integer getId() {
41 | return id;
42 | }
43 |
44 | public void setId(Integer id) {
45 | this.id = id;
46 | }
47 |
48 | public String getName() {
49 | return name;
50 | }
51 |
52 | public String getUsername() {
53 | return username;
54 | }
55 |
56 | public String getDomain() {
57 | return domain;
58 | }
59 |
60 | public String getPassword() {
61 | return password;
62 | }
63 |
64 | public String getProxy() {
65 | return proxy;
66 | }
67 |
68 | public String getTransport() {
69 | return transport;
70 | }
71 |
72 | public String getContactParams() {
73 | return contactParams;
74 | }
75 |
76 | public String getContactUriParams() {
77 | return contactUriParams;
78 | }
79 |
80 | public String getRegServer() {
81 | return regServer;
82 | }
83 |
84 | public Map getRegHeaders() {
85 | return regHeaders;
86 | }
87 |
88 | public String getRegContactParams() {
89 | return regContactParams;
90 | }
91 |
92 | public boolean isRegOnAdd() {
93 | return regOnAdd;
94 | }
95 |
96 | public String getNomalizedRegServer() {
97 | return regServer != null && regServer.length() > 0 ? regServer : "*";
98 | }
99 |
100 | @Nullable
101 | public Integer getRegTimeout() {
102 | return regTimeout;
103 | }
104 |
105 | public String getRegUri() {
106 | return "sip:"+ domain;
107 | }
108 |
109 | public String getIdUri() {
110 | if (name != null) {
111 | return name + " ";
112 | }
113 |
114 | return "";
115 | }
116 |
117 | public boolean isTransportNotEmpty() {
118 | return transport != null && !transport.isEmpty() && !transport.equals("TCP");
119 | }
120 |
121 | public boolean isRegTimeoutNotEmpty() {
122 | return this.regTimeout != null && this.regTimeout != 0;
123 | }
124 |
125 | public boolean isProxyNotEmpty() {
126 | return proxy != null && proxy.length() > 0;
127 | }
128 |
129 | public JSONObject toJson() {
130 | JSONObject json = new JSONObject();
131 | try {
132 | JSONObject registration = new JSONObject();
133 | registration.put("status", "");
134 | registration.put("statusText", "");
135 | registration.put("active", false);
136 | registration.put("reason", "");
137 | json.put("id", getId());
138 | json.put("name", getName());
139 | json.put("username", getUsername());
140 | json.put("domain", getDomain());
141 | json.put("registration", registration);
142 | return json;
143 | } catch (Exception e) {
144 | throw new RuntimeException(e);
145 | }
146 | }
147 |
148 | public static AccountConfigurationDTO fromIntent(Intent intent) {
149 | AccountConfigurationDTO c = new AccountConfigurationDTO();
150 | c.name = intent.getStringExtra("name");
151 | c.username = intent.getStringExtra("username");
152 | c.domain = intent.getStringExtra("domain");
153 | c.password = intent.getStringExtra("password");
154 | c.proxy = intent.getStringExtra("proxy");
155 | c.transport = intent.getStringExtra("transport");
156 | c.contactParams = intent.getStringExtra("contactParams");
157 | c.contactUriParams = intent.getStringExtra("contactUriParams");
158 |
159 | c.regServer = intent.getStringExtra("regServer");
160 | c.regTimeout = 600;
161 | c.regOnAdd = intent.getBooleanExtra("regOnAdd", true);
162 |
163 | if (intent.hasExtra("regTimeout")) {
164 | String regTimeout = intent.getStringExtra("regTimeout");
165 |
166 | if (regTimeout != null && !regTimeout.isEmpty()) {
167 | int timeout = Integer.parseInt(regTimeout);
168 | if (timeout > 0) {
169 | c.regTimeout = timeout;
170 | }
171 | }
172 | }
173 |
174 | c.regContactParams = intent.getStringExtra("regContactParams");
175 |
176 | if (intent.hasExtra("regHeaders")) {
177 | c.regHeaders = (Map) intent.getSerializableExtra("regHeaders");
178 | }
179 |
180 | return c;
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/dto/CallSettingsDTO.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip.dto;
2 |
3 | import com.facebook.react.bridge.ReadableMap;
4 | import com.google.gson.Gson;
5 |
6 | public class CallSettingsDTO {
7 | private Integer audioCount;
8 | private Integer videoCount;
9 | private Integer flag;
10 | private Integer requestKeyframeMethod;
11 |
12 | public Integer getAudioCount() {
13 | return audioCount;
14 | }
15 |
16 | public Integer getVideoCount() {
17 | return videoCount;
18 | }
19 |
20 | public Integer getFlag() {
21 | return flag;
22 | }
23 |
24 | public Integer getRequestKeyframeMethod() {
25 | return requestKeyframeMethod;
26 | }
27 |
28 | public void setAudioCount(Integer audioCount) {
29 | this.audioCount = audioCount;
30 | }
31 |
32 | public void setVideoCount(Integer videoCount) {
33 | this.videoCount = videoCount;
34 | }
35 |
36 | public void setFlag(Integer flag) {
37 | this.flag = flag;
38 | }
39 |
40 | public void setRequestKeyframeMethod(Integer requestKeyframeMethod) {
41 | this.requestKeyframeMethod = requestKeyframeMethod;
42 | }
43 |
44 | public String toJson () {
45 | Gson gson = new Gson();
46 | return gson.toJson(this);
47 | }
48 |
49 | public static CallSettingsDTO fromJson(String json) {
50 | Gson gson = new Gson();
51 | return gson.fromJson(json, CallSettingsDTO.class);
52 | }
53 |
54 | public static CallSettingsDTO fromReadableMap(ReadableMap data) {
55 | CallSettingsDTO result = new CallSettingsDTO();
56 |
57 | if (data.hasKey("audioCount")) {
58 | result.setAudioCount(data.getInt("audioCount"));
59 | }
60 | if (data.hasKey("videoCount")) {
61 | result.setVideoCount(data.getInt("videoCount"));
62 | }
63 | if (data.hasKey("flag")) {
64 | result.setFlag(data.getInt("flag"));
65 | }
66 | if (data.hasKey("requestKeyframeMethod")) {
67 | result.setRequestKeyframeMethod(data.getInt("requestKeyframeMethod"));
68 | }
69 |
70 | return result;
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/dto/ServiceConfigurationDTO.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip.dto;
2 |
3 | import android.content.Intent;
4 |
5 | import com.facebook.react.bridge.ReadableMap;
6 | import org.json.JSONObject;
7 | import org.pjsip.pjsua2.StringVector;
8 |
9 | import java.util.Map;
10 | import java.util.ArrayList;
11 |
12 | public class ServiceConfigurationDTO {
13 |
14 | public String ua;
15 | public ArrayList stun;
16 |
17 | public String getUserAgent() {
18 | return ua;
19 | }
20 |
21 | public StringVector getStunServers() {
22 | StringVector serversVector = new StringVector();
23 | for (String server : stun) {
24 | serversVector.add(server);
25 | }
26 | return serversVector;
27 | }
28 |
29 | public boolean isUserAgentNotEmpty() {
30 | return ua != null && !ua.isEmpty();
31 | }
32 |
33 | public boolean isStunServersNotEmpty() {
34 | return stun != null && stun.size() > 0;
35 | }
36 |
37 | public JSONObject toJson() {
38 | JSONObject json = new JSONObject();
39 |
40 | try {
41 | json.put("ua", ua);
42 |
43 | return json;
44 | } catch (Exception e) {
45 | throw new RuntimeException(e);
46 | }
47 | }
48 |
49 | public static ServiceConfigurationDTO fromIntent(Intent intent) {
50 | ServiceConfigurationDTO c = new ServiceConfigurationDTO();
51 |
52 | if (intent.hasExtra("ua")) {
53 | c.ua = intent.getStringExtra("ua");
54 | }
55 |
56 | return c;
57 | }
58 |
59 | public static ServiceConfigurationDTO fromMap(Map conf) {
60 | ServiceConfigurationDTO c = new ServiceConfigurationDTO();
61 |
62 | if (conf.containsKey("ua")) {
63 | c.ua = conf.get("ua").toString();
64 | }
65 |
66 | if (conf.containsKey("stun")) {
67 | c.stun = (ArrayList) conf.get("stun");
68 | }
69 |
70 | return c;
71 | }
72 |
73 | public static ServiceConfigurationDTO fromConfiguration(ReadableMap data) {
74 | ServiceConfigurationDTO c = new ServiceConfigurationDTO();
75 |
76 | if (data.hasKey("ua")) {
77 | c.ua = data.getString("ua");
78 | }
79 |
80 | return c;
81 | }
82 |
83 | @Override
84 | public boolean equals(Object o) {
85 | if (this == o) return true;
86 | if (o == null || getClass() != o.getClass()) return false;
87 |
88 | ServiceConfigurationDTO that = (ServiceConfigurationDTO) o;
89 |
90 | return ua != null ? ua.equals(that.ua) : that.ua == null;
91 | }
92 |
93 | @Override
94 | public int hashCode() {
95 | return ua != null ? ua.hashCode() : 0;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/dto/SipMessageDTO.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip.dto;
2 |
3 | import com.facebook.react.bridge.ReadableMap;
4 | import com.facebook.react.bridge.ReadableMapKeySetIterator;
5 | import com.google.gson.Gson;
6 |
7 | import org.pjsip.pjsua2.SipHeaderVector;
8 |
9 | import java.util.HashMap;
10 | import java.util.Map;
11 |
12 | public class SipMessageDTO {
13 |
14 | private String targetUri;
15 | private Map headers;
16 | private String contentType;
17 | private String body;
18 |
19 | public String getTargetUri() {
20 | return targetUri;
21 | }
22 |
23 | public void setTargetUri(String targetUri) {
24 | this.targetUri = targetUri;
25 | }
26 |
27 | public Map getHeaders() {
28 | return headers;
29 | }
30 |
31 | public void setHeaders(Map headers) {
32 | this.headers = headers;
33 | }
34 |
35 | public String getContentType() {
36 | return contentType;
37 | }
38 |
39 | public void setContentType(String contentType) {
40 | this.contentType = contentType;
41 | }
42 |
43 | public String getBody() {
44 | return body;
45 | }
46 |
47 | public void setBody(String body) {
48 | this.body = body;
49 | }
50 |
51 | public String toJson () {
52 | Gson gson = new Gson();
53 | return gson.toJson(this);
54 | }
55 |
56 | public static SipMessageDTO fromJson(String json) {
57 | Gson gson = new Gson();
58 | return gson.fromJson(json, SipMessageDTO.class);
59 | }
60 |
61 | public static SipMessageDTO fromReadableMap(ReadableMap data) {
62 | SipMessageDTO result = new SipMessageDTO();
63 |
64 | if (data.hasKey("targetURI")) {
65 | result.setTargetUri(data.getString("targetURI"));
66 | }
67 | if (data.hasKey("headers")) {
68 | ReadableMap headersData = data.getMap("headers");
69 | ReadableMapKeySetIterator headersIt = headersData.keySetIterator();
70 | Map headers = new HashMap<>();
71 |
72 | while (headersIt.hasNextKey()) {
73 | String key = headersIt.nextKey();
74 | headers.put(key, headersData.getString(key));
75 | }
76 |
77 | result.setHeaders(headers);
78 | }
79 | if (data.hasKey("contentType")) {
80 | result.setContentType(data.getString("contentType"));
81 | }
82 | if (data.hasKey("body")) {
83 | result.setBody(data.getString("body"));
84 | }
85 |
86 | return result;
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/android/src/main/java/com/carusto/ReactNativePjSip/utils/ArgumentUtils.java:
--------------------------------------------------------------------------------
1 | package com.carusto.ReactNativePjSip.utils;
2 |
3 | import android.content.Intent;
4 | import android.util.Log;
5 | import com.facebook.react.bridge.WritableArray;
6 | import com.facebook.react.bridge.WritableMap;
7 | import com.facebook.react.bridge.WritableNativeArray;
8 | import com.facebook.react.bridge.WritableNativeMap;
9 | import com.google.gson.*;
10 | import com.google.gson.internal.LazilyParsedNumber;
11 |
12 | import java.util.HashMap;
13 | import java.util.Map;
14 | import java.util.Set;
15 |
16 | public class ArgumentUtils {
17 |
18 | public static Object fromJson(String json) {
19 | return fromJson(new JsonParser().parse(json));
20 | }
21 |
22 | private static Object fromJson(JsonElement el) {
23 | if (el instanceof JsonObject) {
24 | return fromJsonObject(el.getAsJsonObject());
25 | } else if (el instanceof JsonArray) {
26 | return fromJsonArray(el.getAsJsonArray());
27 | } else {
28 | return fromJsonPrimitive(el.getAsJsonPrimitive());
29 | }
30 | }
31 |
32 | private static WritableMap fromJsonObject(JsonObject object) {
33 | WritableMap result = new WritableNativeMap();
34 |
35 | for (Map.Entry entry : object.entrySet()) {
36 | Object value = fromJson(entry.getValue());
37 |
38 | if (value instanceof WritableMap) {
39 | result.putMap(entry.getKey(), (WritableMap) value);
40 | } else if (value instanceof WritableArray) {
41 | result.putArray(entry.getKey(), (WritableArray) value);
42 | } else if (value instanceof String) {
43 | result.putString(entry.getKey(), (String) value);
44 | } else if (value instanceof LazilyParsedNumber) {
45 | result.putInt(entry.getKey(), ((LazilyParsedNumber) value).intValue());
46 | } else if (value instanceof Integer) {
47 | result.putInt(entry.getKey(), (Integer) value);
48 | } else if (value instanceof Double) {
49 | result.putDouble(entry.getKey(), (Double) value);
50 | } else if (value instanceof Boolean) {
51 | result.putBoolean(entry.getKey(), (Boolean) value);
52 | } else {
53 | Log.d("ArgumentUtils", "Unknown type: " + value.getClass().getName());
54 | result.putNull(entry.getKey());
55 | }
56 | }
57 |
58 | return result;
59 | }
60 |
61 | private static Object fromJsonPrimitive(JsonPrimitive object) {
62 | if (object.isString()) {
63 | return object.getAsString();
64 | } else if (object.isNumber()) {
65 | return object.getAsNumber();
66 | } else if (object.isBoolean()) {
67 | return object.getAsBoolean();
68 | }
69 |
70 | return null;
71 | }
72 |
73 | private static WritableArray fromJsonArray(JsonArray arr) {
74 | WritableArray result = new WritableNativeArray();
75 |
76 | for (JsonElement el : arr) {
77 | Object item = fromJson(el);
78 |
79 | if (item instanceof WritableMap) {
80 | result.pushMap((WritableMap) item);
81 | } else if (item instanceof WritableArray) {
82 | result.pushArray((WritableArray) item);
83 | } else if (item instanceof String) {
84 | result.pushString((String) item);
85 | } else if (item instanceof LazilyParsedNumber) {
86 | result.pushInt(((LazilyParsedNumber) item).intValue());
87 | } else if (item instanceof Integer) {
88 | result.pushInt((Integer) item);
89 | } else if (item instanceof Double) {
90 | result.pushDouble((Double) item);
91 | } else if (item instanceof Boolean) {
92 | result.pushBoolean((Boolean) item);
93 | } else {
94 |
95 | Log.d("ArgumentUtils", "Unknown type: " + item.getClass().getName());
96 |
97 | result.pushNull();
98 | }
99 | }
100 |
101 | return result;
102 | }
103 |
104 |
105 | public static String dumpIntentExtraParameters(Intent intent) {
106 | if (intent == null || intent.getExtras() == null) {
107 | return "empty extras";
108 | }
109 |
110 | Set keys = intent.getExtras().keySet();
111 | Map data = new HashMap<>(keys.size());
112 |
113 | for (String key : keys) {
114 | data.put(key, intent.getExtras().get(key));
115 | }
116 |
117 | Gson gson = new Gson();
118 | return gson.toJson(data);
119 | }
120 |
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/android/src/main/main.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/android/src/main/res/drawable-hdpi/ic_notif.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/android/src/main/res/drawable-hdpi/ic_notif.png
--------------------------------------------------------------------------------
/android/src/main/res/drawable-mdpi/ic_notif.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/android/src/main/res/drawable-mdpi/ic_notif.png
--------------------------------------------------------------------------------
/android/src/main/res/drawable-xhdpi/ic_notif.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/android/src/main/res/drawable-xhdpi/ic_notif.png
--------------------------------------------------------------------------------
/android/src/main/res/drawable-xxhdpi/ic_notif.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/android/src/main/res/drawable-xxhdpi/ic_notif.png
--------------------------------------------------------------------------------
/android/src/main/res/raw/ring.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/android/src/main/res/raw/ring.wav
--------------------------------------------------------------------------------
/android/src/main/res/raw/ringback.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/android/src/main/res/raw/ringback.wav
--------------------------------------------------------------------------------
/build/Account.d.ts:
--------------------------------------------------------------------------------
1 | import AccountRegistration from './AccountRegistration';
2 | export declare type AccountConfiguration = {
3 | id: number;
4 | uri: string;
5 | name: string;
6 | username: string;
7 | domain: string | null;
8 | password: string;
9 | proxy: string;
10 | transport: string;
11 | contactParams: string;
12 | contactUriParams: string;
13 | regServer: string;
14 | regTimeout: string;
15 | regContactParams: string;
16 | regHeaders: Object;
17 | };
18 | /**
19 | * This describes account configuration and registration status
20 | */
21 | export default class Account {
22 | _data: AccountConfiguration;
23 | _registration: AccountRegistration;
24 | constructor(data: any);
25 | /**
26 | * The account ID.
27 | * @returns {int}
28 | */
29 | getId(): number;
30 | /**
31 | * This is the URL to be put in the request URI for the registration, and will look something like "sip:serviceprovider".
32 | * @returns {String}
33 | */
34 | getURI(): string;
35 | /**
36 | * Full name specified in Endpoint.createAccount().
37 | * @returns {String}
38 | */
39 | getName(): string;
40 | /**
41 | * Username specified in Endpoint.createAccount().
42 | * @returns {String}
43 | */
44 | getUsername(): string;
45 | /**
46 | * Domain specified in Endpoint.createAccount().
47 | * @returns {int|null}
48 | */
49 | getDomain(): string | null;
50 | /**
51 | * Password specified in Endpoint.createAccount().
52 | * @returns {String}
53 | */
54 | getPassword(): string;
55 | /**
56 | * Proxy specified in Endpoint.createAccount().
57 | * @returns {String}
58 | */
59 | getProxy(): string;
60 | /**
61 | * Transport specified in Endpoint.createAccount().
62 | * @returns {String}
63 | */
64 | getTransport(): string;
65 | /**
66 | * Additional parameters that will be appended in the Contact header
67 | * for this account.
68 | * @returns {String}
69 | */
70 | getContactParams(): string;
71 | /**
72 | * Additional URI parameters that will be appended in the Contact URI
73 | * for this account.
74 | * @returns {String}
75 | */
76 | getContactUriParams(): string;
77 | /**
78 | * Port specified in Endpoint.createAccount().
79 | * @returns {String}
80 | */
81 | getRegServer(): string;
82 | /**
83 | * Port specified in Endpoint.createAccount().
84 | * @returns {String}
85 | */
86 | getRegTimeout(): string;
87 | /**
88 | * @returns {String}
89 | */
90 | getRegContactParams(): string;
91 | /**
92 | * @returns {Object}
93 | */
94 | getRegHeaders(): Object;
95 | /**
96 | * Account registration status.
97 | * @returns {AccountRegistration}
98 | */
99 | getRegistration(): AccountRegistration;
100 | }
101 |
--------------------------------------------------------------------------------
/build/Account.js:
--------------------------------------------------------------------------------
1 | import AccountRegistration from './AccountRegistration';
2 | /**
3 | * This describes account configuration and registration status
4 | */
5 | export default class Account {
6 | constructor(data) {
7 | this._data = data;
8 | this._registration = new AccountRegistration(data['registration']);
9 | }
10 | /**
11 | * The account ID.
12 | * @returns {int}
13 | */
14 | getId() {
15 | return this._data.id;
16 | }
17 | /**
18 | * This is the URL to be put in the request URI for the registration, and will look something like "sip:serviceprovider".
19 | * @returns {String}
20 | */
21 | getURI() {
22 | return this._data.uri;
23 | }
24 | /**
25 | * Full name specified in Endpoint.createAccount().
26 | * @returns {String}
27 | */
28 | getName() {
29 | return this._data.name;
30 | }
31 | /**
32 | * Username specified in Endpoint.createAccount().
33 | * @returns {String}
34 | */
35 | getUsername() {
36 | return this._data.username;
37 | }
38 | /**
39 | * Domain specified in Endpoint.createAccount().
40 | * @returns {int|null}
41 | */
42 | getDomain() {
43 | return this._data.domain;
44 | }
45 | /**
46 | * Password specified in Endpoint.createAccount().
47 | * @returns {String}
48 | */
49 | getPassword() {
50 | return this._data.password;
51 | }
52 | /**
53 | * Proxy specified in Endpoint.createAccount().
54 | * @returns {String}
55 | */
56 | getProxy() {
57 | return this._data.proxy;
58 | }
59 | /**
60 | * Transport specified in Endpoint.createAccount().
61 | * @returns {String}
62 | */
63 | getTransport() {
64 | return this._data.transport;
65 | }
66 | /**
67 | * Additional parameters that will be appended in the Contact header
68 | * for this account.
69 | * @returns {String}
70 | */
71 | getContactParams() {
72 | return this._data.contactParams;
73 | }
74 | /**
75 | * Additional URI parameters that will be appended in the Contact URI
76 | * for this account.
77 | * @returns {String}
78 | */
79 | getContactUriParams() {
80 | return this._data.contactUriParams;
81 | }
82 | /**
83 | * Port specified in Endpoint.createAccount().
84 | * @returns {String}
85 | */
86 | getRegServer() {
87 | return this._data.regServer || "";
88 | }
89 | /**
90 | * Port specified in Endpoint.createAccount().
91 | * @returns {String}
92 | */
93 | getRegTimeout() {
94 | return this._data.regTimeout;
95 | }
96 | /**
97 | * @returns {String}
98 | */
99 | getRegContactParams() {
100 | return this._data.regContactParams;
101 | }
102 | /**
103 | * @returns {Object}
104 | */
105 | getRegHeaders() {
106 | return this._data.regHeaders;
107 | }
108 | /**
109 | * Account registration status.
110 | * @returns {AccountRegistration}
111 | */
112 | getRegistration() {
113 | return this._registration;
114 | }
115 | }
116 | //# sourceMappingURL=Account.js.map
--------------------------------------------------------------------------------
/build/Account.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"Account.js","sourceRoot":"","sources":["../src/Account.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,MAAM,uBAAuB,CAAA;AAmBvD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,OAAO;IAIxB,YAAY,IAAS;QACjB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,aAAa,GAAG,IAAI,mBAAmB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;IACvE,CAAC;IAED;;;OAGG;IACH,KAAK;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,MAAM;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;IAC1B,CAAC;IAED;;;OAGG;IACH,OAAO;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,WAAW;QACP,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACH,SAAS;QACL,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACH,WAAW;QACP,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACH,QAAQ;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACH,YAAY;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;IAChC,CAAC;IAED;;;;OAIG;IACH,gBAAgB;QACZ,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;IACpC,CAAC;IAED;;;;OAIG;IACH,mBAAmB;QACf,OAAO,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC;IACvC,CAAC;IAED;;;OAGG;IACH,YAAY;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC;IACtC,CAAC;IAED;;;OAGG;IACH,aAAa;QACT,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,mBAAmB;QACf,OAAO,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC;IACvC,CAAC;IAED;;OAEG;IACH,aAAa;QACT,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;IACjC,CAAC;IAED;;;OAGG;IACH,eAAe;QACX,OAAO,IAAI,CAAC,aAAa,CAAC;IAC9B,CAAC;CACJ"}
--------------------------------------------------------------------------------
/build/AccountRegistration.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Account registration information. Application can query the registration info
3 | * by calling account.getRegistration().
4 | */
5 | export default class AccountRegistration {
6 | _status: string;
7 | _statusText: string;
8 | _active: boolean;
9 | _reason: string;
10 | constructor({ status, statusText, active, reason }: {
11 | status: any;
12 | statusText: any;
13 | active: any;
14 | reason: any;
15 | });
16 | /**
17 | * Last registration status code (SIP status codes according to RFC 3261).
18 | * If status code is empty, the account is currently not registered. Any other value indicates the SIP
19 | * status code of the registration.
20 | *
21 | * @returns {string|null}
22 | */
23 | getStatus(): string | null;
24 | /**
25 | * String describing the registration status.
26 | *
27 | * @returns {string|null}
28 | */
29 | getStatusText(): string | null;
30 | /**
31 | * Flag to tell whether this account is currently registered
32 | * (has active registration session).
33 | *
34 | * @returns boolean
35 | */
36 | isActive(): boolean;
37 | /**
38 | * Reason phrase received.
39 | *
40 | * @returns {String|null}
41 | */
42 | getReason(): string | null;
43 | toJson(): {
44 | status: string;
45 | statusText: string;
46 | active: boolean;
47 | reason: string;
48 | };
49 | }
50 |
--------------------------------------------------------------------------------
/build/AccountRegistration.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Account registration information. Application can query the registration info
3 | * by calling account.getRegistration().
4 | */
5 | export default class AccountRegistration {
6 | constructor({ status, statusText, active, reason }) {
7 | this._status = status;
8 | this._statusText = statusText;
9 | this._active = active;
10 | this._reason = reason;
11 | }
12 | /**
13 | * Last registration status code (SIP status codes according to RFC 3261).
14 | * If status code is empty, the account is currently not registered. Any other value indicates the SIP
15 | * status code of the registration.
16 | *
17 | * @returns {string|null}
18 | */
19 | getStatus() {
20 | return this._status;
21 | }
22 | /**
23 | * String describing the registration status.
24 | *
25 | * @returns {string|null}
26 | */
27 | getStatusText() {
28 | return this._statusText;
29 | }
30 | /**
31 | * Flag to tell whether this account is currently registered
32 | * (has active registration session).
33 | *
34 | * @returns boolean
35 | */
36 | isActive() {
37 | return this._active;
38 | }
39 | /**
40 | * Reason phrase received.
41 | *
42 | * @returns {String|null}
43 | */
44 | getReason() {
45 | return this._reason;
46 | }
47 | toJson() {
48 | return {
49 | status: this._status,
50 | statusText: this._statusText,
51 | active: this._active,
52 | reason: this._reason
53 | };
54 | }
55 | }
56 | //# sourceMappingURL=AccountRegistration.js.map
--------------------------------------------------------------------------------
/build/AccountRegistration.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"AccountRegistration.js","sourceRoot":"","sources":["../src/AccountRegistration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAmB;IAMpC,YAAY,EAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAC;QAC5C,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAC9B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IAC1B,CAAC;IAED;;;;;;OAMG;IACH,SAAS;QACL,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACH,aAAa;QACT,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED;;;;;OAKG;IACH,QAAQ;QACJ,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACH,SAAS;QACL,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;IAED,MAAM;QAMF,OAAQ;YACJ,MAAM,EAAE,IAAI,CAAC,OAAO;YACpB,UAAU,EAAE,IAAI,CAAC,WAAW;YAC5B,MAAM,EAAE,IAAI,CAAC,OAAO;YACpB,MAAM,EAAE,IAAI,CAAC,OAAO;SACvB,CAAA;IACL,CAAC;CACJ"}
--------------------------------------------------------------------------------
/build/Call.d.ts:
--------------------------------------------------------------------------------
1 | export declare enum CallState {
2 | PJSIP_INV_STATE_NULL = "PJSIP_INV_STATE_NULL",
3 | PJSIP_INV_STATE_CALLING = "PJSIP_INV_STATE_CALLING",
4 | PJSIP_INV_STATE_INCOMING = "PJSIP_INV_STATE_INCOMING",
5 | PJSIP_INV_STATE_EARLY = "PJSIP_INV_STATE_EARLY",
6 | PJSIP_INV_STATE_CONNECTING = "PJSIP_INV_STATE_CONNECTING",
7 | PJSIP_INV_STATE_CONFIRMED = "PJSIP_INV_STATE_CONFIRMED",
8 | PJSIP_INV_STATE_DISCONNECTED = "PJSIP_INV_STATE_DISCONNECTED"
9 | }
10 | export declare type CallData = {
11 | id: number;
12 | callId: string;
13 | accountId: number;
14 | localContact: string;
15 | localUri: string;
16 | remoteContact: string;
17 | remoteUri: string;
18 | state: CallState;
19 | stateText: string;
20 | held: boolean;
21 | muted: boolean;
22 | speaker: boolean;
23 | connectDuration: number;
24 | totalDuration: number;
25 | remoteOfferer: number;
26 | remoteAudioCount: number;
27 | remoteVideoCount: number;
28 | remoteNumber: string;
29 | remoteName: string;
30 | audioCount: number;
31 | videoCount: number;
32 | lastStatusCode: string;
33 | lastReason: string;
34 | media: string;
35 | provisionalMedia: string;
36 | };
37 | /**
38 | * This class describes the information and current status of a call.
39 | */
40 | declare class Call {
41 | _id: number;
42 | _callId: string;
43 | _accountId: number;
44 | _localContact: string;
45 | _localUri: string;
46 | _remoteContact: string;
47 | _remoteUri: string;
48 | _state: CallState;
49 | _stateText: string;
50 | _held: boolean;
51 | _muted: boolean;
52 | _speaker: boolean;
53 | _connectDuration: number;
54 | _totalDuration: number;
55 | _remoteOfferer: number;
56 | _remoteAudioCount: number;
57 | _remoteVideoCount: number;
58 | _remoteNumber: string;
59 | _remoteName: string;
60 | _audioCount: number;
61 | _videoCount: number;
62 | _lastStatusCode: string;
63 | _lastReason: string;
64 | _media: string;
65 | _provisionalMedia: string;
66 | _constructionTime: number;
67 | constructor({ id, callId, accountId, localContact, localUri, remoteContact, remoteUri, state, stateText, held, muted, speaker, connectDuration, totalDuration, remoteOfferer, remoteAudioCount, remoteVideoCount, audioCount, videoCount, lastStatusCode, lastReason, media, provisionalMedia, }: CallData);
68 | /**
69 | * Call identification.
70 | * @returns {int}
71 | */
72 | getId(): number;
73 | /**
74 | * The account ID where this call belongs.
75 | * @returns {int}
76 | */
77 | getAccountId(): number;
78 | /**
79 | * Dialog Call-ID string.
80 | *
81 | * @returns {String}
82 | */
83 | getCallId(): string;
84 | /**
85 | * Up-to-date call duration in seconds.
86 | * Use local time to calculate actual call duration.
87 | *
88 | * @public
89 | * @returns {int}
90 | */
91 | getTotalDuration(): number;
92 | /**
93 | * Up-to-date call connected duration (zero when call is not established)
94 | *
95 | * @returns {int}
96 | */
97 | getConnectDuration(): number;
98 | /**
99 | * Call duration in "MM:SS" format.
100 | *
101 | * @public
102 | * @returns {string}
103 | */
104 | getFormattedTotalDuration(): string;
105 | /**
106 | * Call duration in "MM:SS" format.
107 | *
108 | * @public
109 | * @returns {string}
110 | */
111 | getFormattedConnectDuration(): string;
112 | /**
113 | * Local Contact.
114 | * TODO: Provide example
115 | * @returns {String}
116 | */
117 | getLocalContact(): string;
118 | /**
119 | * Local URI.
120 | * TODO: Provide example
121 | * @returns {String}
122 | */
123 | getLocalUri(): string;
124 | /**
125 | * Remote contact.
126 | * TODO: Provide example
127 | * @returns {String}
128 | */
129 | getRemoteContact(): string;
130 | /**
131 | * Remote URI.
132 | * TODO: Provide example
133 | * @returns {String}
134 | */
135 | getRemoteUri(): string;
136 | /**
137 | * Callee name. Could be null if no name specified in URI.
138 | * @returns {String}
139 | */
140 | getRemoteName(): string;
141 | /**
142 | * Callee number
143 | * @returns {String}
144 | */
145 | getRemoteNumber(): string;
146 | /**
147 | * @returns {String}
148 | */
149 | getRemoteFormattedNumber(): string;
150 | /**
151 | * Invite session state.
152 | *
153 | * PJSIP_INV_STATE_NULL Before INVITE is sent or received
154 | * PJSIP_INV_STATE_CALLING After INVITE is sent
155 | * PJSIP_INV_STATE_INCOMING After INVITE is received.
156 | * PJSIP_INV_STATE_EARLY After response with To tag.
157 | * PJSIP_INV_STATE_CONNECTING After 2xx is sent/received.
158 | * PJSIP_INV_STATE_CONFIRMED After ACK is sent/received.
159 | * PJSIP_INV_STATE_DISCONNECTED Session is terminated.
160 | *
161 | * @returns {String}
162 | */
163 | getState(): CallState;
164 | /**
165 | * Text describing the state.
166 | *
167 | * @returns {String}
168 | */
169 | getStateText(): string;
170 | isHeld(): boolean;
171 | isMuted(): boolean;
172 | isSpeaker(): boolean;
173 | isTerminated(): boolean;
174 | /**
175 | * Flag if remote was SDP offerer
176 | * @returns {boolean}
177 | */
178 | getRemoteOfferer(): number;
179 | /**
180 | * Number of audio streams offered by remote.
181 | * @returns {int}
182 | */
183 | getRemoteAudioCount(): number;
184 | /**
185 | * Number of video streams offered by remote.
186 | * @returns {int}
187 | */
188 | getRemoteVideoCount(): number;
189 | /**
190 | * Number of simultaneous active audio streams for this call. If zero - audio is disabled in this call.
191 | * @returns {int}
192 | */
193 | getAudioCount(): number;
194 | /**
195 | * Number of simultaneous active video streams for this call. If zero - video is disabled in this call.
196 | * @returns {*}
197 | */
198 | getVideoCount(): number;
199 | /**
200 | * Last status code heard, which can be used as cause code.
201 | * Possible values:
202 | * - PJSIP_SC_TRYING / 100
203 | * - PJSIP_SC_RINGING / 180
204 | * - PJSIP_SC_CALL_BEING_FORWARDED / 181
205 | * - PJSIP_SC_QUEUED / 182
206 | * - PJSIP_SC_PROGRESS / 183
207 | * - PJSIP_SC_OK / 200
208 | * - PJSIP_SC_ACCEPTED / 202
209 | * - PJSIP_SC_MULTIPLE_CHOICES / 300
210 | * - PJSIP_SC_MOVED_PERMANENTLY / 301
211 | * - PJSIP_SC_MOVED_TEMPORARILY / 302
212 | * - PJSIP_SC_USE_PROXY / 305
213 | * - PJSIP_SC_ALTERNATIVE_SERVICE / 380
214 | * - PJSIP_SC_BAD_REQUEST / 400
215 | * - PJSIP_SC_UNAUTHORIZED / 401
216 | * - PJSIP_SC_PAYMENT_REQUIRED / 402
217 | * - PJSIP_SC_FORBIDDEN / 403
218 | * - PJSIP_SC_NOT_FOUND / 404
219 | * - PJSIP_SC_METHOD_NOT_ALLOWED / 405
220 | * - PJSIP_SC_NOT_ACCEPTABLE / 406
221 | * - PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED / 407
222 | * - PJSIP_SC_REQUEST_TIMEOUT / 408
223 | * - PJSIP_SC_GONE / 410
224 | * - PJSIP_SC_REQUEST_ENTITY_TOO_LARGE / 413
225 | * - PJSIP_SC_REQUEST_URI_TOO_LONG / 414
226 | * - PJSIP_SC_UNSUPPORTED_MEDIA_TYPE / 415
227 | * - PJSIP_SC_UNSUPPORTED_URI_SCHEME / 416
228 | * - PJSIP_SC_BAD_EXTENSION / 420
229 | * - PJSIP_SC_EXTENSION_REQUIRED / 421
230 | * - PJSIP_SC_SESSION_TIMER_TOO_SMALL / 422
231 | * - PJSIP_SC_INTERVAL_TOO_BRIEF / 423
232 | * - PJSIP_SC_TEMPORARILY_UNAVAILABLE / 480
233 | * - PJSIP_SC_CALL_TSX_DOES_NOT_EXIST / 481
234 | * - PJSIP_SC_LOOP_DETECTED / 482
235 | * - PJSIP_SC_TOO_MANY_HOPS / 483
236 | * - PJSIP_SC_ADDRESS_INCOMPLETE / 484
237 | * - PJSIP_AC_AMBIGUOUS / 485
238 | * - PJSIP_SC_BUSY_HERE / 486
239 | * - PJSIP_SC_REQUEST_TERMINATED / 487
240 | * - PJSIP_SC_NOT_ACCEPTABLE_HERE / 488
241 | * - PJSIP_SC_BAD_EVENT / 489
242 | * - PJSIP_SC_REQUEST_UPDATED / 490
243 | * - PJSIP_SC_REQUEST_PENDING / 491
244 | * - PJSIP_SC_UNDECIPHERABLE / 493
245 | * - PJSIP_SC_INTERNAL_SERVER_ERROR / 500
246 | * - PJSIP_SC_NOT_IMPLEMENTED / 501
247 | * - PJSIP_SC_BAD_GATEWAY / 502
248 | * - PJSIP_SC_SERVICE_UNAVAILABLE / 503
249 | * - PJSIP_SC_SERVER_TIMEOUT / 504
250 | * - PJSIP_SC_VERSION_NOT_SUPPORTED / 505
251 | * - PJSIP_SC_MESSAGE_TOO_LARGE / 513
252 | * - PJSIP_SC_PRECONDITION_FAILURE / 580
253 | * - PJSIP_SC_BUSY_EVERYWHERE / 600
254 | * - PJSIP_SC_DECLINE / 603
255 | * - PJSIP_SC_DOES_NOT_EXIST_ANYWHERE / 604
256 | * - PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE / 606
257 | * - PJSIP_SC_TSX_TIMEOUT / PJSIP_SC_REQUEST_TIMEOUT
258 | * - PJSIP_SC_TSX_TRANSPORT_ERROR / PJSIP_SC_SERVICE_UNAVAILABLE
259 | *
260 | * @returns {string}
261 | */
262 | getLastStatusCode(): string;
263 | /**
264 | * The reason phrase describing the last status.
265 | *
266 | * @returns {string}
267 | */
268 | getLastReason(): string;
269 | getMedia(): string;
270 | getProvisionalMedia(): string;
271 | /**
272 | * Format seconds to "MM:SS" format.
273 | *
274 | * @public
275 | * @returns {string}
276 | */
277 | _formatTime(seconds: number): string;
278 | }
279 | export { Call };
280 | export default Call;
281 |
--------------------------------------------------------------------------------
/build/Call.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"Call.js","sourceRoot":"","sources":["../src/Call.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,SAQX;AARD,WAAY,SAAS;IACjB,0DAA6C,CAAA;IAC7C,gEAAmD,CAAA;IACnD,kEAAqD,CAAA;IACrD,4DAA+C,CAAA;IAC/C,sEAAyD,CAAA;IACzD,oEAAuD,CAAA;IACvD,0EAA6D,CAAA;AACjE,CAAC,EARW,SAAS,KAAT,SAAS,QAQpB;AAAA,CAAC;AA8BF;;GAEG;AACH,MAAM,IAAI;IA6BN,YAAY,EACJ,EAAE,EACF,MAAM,EACN,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,SAAS,EACT,KAAK,EACL,SAAS,EACT,IAAI,EACJ,KAAK,EACL,OAAO,EACP,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,cAAc,EACd,UAAU,EACV,KAAK,EACL,gBAAgB,GACT;QACX,IAAI,YAAY,GAAG,IAAI,CAAC;QACxB,IAAI,UAAU,GAAG,IAAI,CAAC;QAEtB,IAAI,SAAS,EAAE;YACX,IAAI,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;YAEvD,IAAI,KAAK,EAAE;gBACP,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBACtB,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;aAC3B;iBAAM;gBACH,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;gBAExC,IAAI,KAAK,EAAE;oBACP,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;iBAC3B;aACJ;SACJ;QAED,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;QACd,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;QAClC,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;QAC1B,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;QACpC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,gBAAgB,GAAG,eAAe,CAAC;QACxC,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;QACpC,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;QACpC,IAAI,CAAC,iBAAiB,GAAG,gBAAgB,CAAC;QAC1C,IAAI,CAAC,iBAAiB,GAAG,gBAAgB,CAAC;QAC1C,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;QAClC,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAC9B,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAC9B,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAC9B,IAAI,CAAC,eAAe,GAAG,cAAc,CAAC;QACtC,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAE9B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,iBAAiB,GAAG,gBAAgB,CAAC;QAE1C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACrE,CAAC;IAED;;;OAGG;IACH,KAAK;QACD,OAAO,IAAI,CAAC,GAAG,CAAC;IACpB,CAAC;IAED;;;OAGG;IACH,YAAY;QACR,OAAO,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACH,SAAS;QACL,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;IAGD;;;;;;OAMG;IACH,gBAAgB;QACZ,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACnD,IAAI,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC;QAE3C,OAAO,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC;IACxC,CAAC;IAAA,CAAC;IAEF;;;;OAIG;IACH,kBAAkB;QACd,IAAI,IAAI,CAAC,gBAAgB,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,8BAA8B,EAAE;YAC5E,OAAO,IAAI,CAAC,gBAAgB,CAAC;SAChC;QAED,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACnD,IAAI,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC;QAE3C,OAAO,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC;IAC1C,CAAC;IAED;;;;;OAKG;IACH,yBAAyB;QACrB,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACrD,CAAC;IAAA,CAAC;IAEF;;;;;OAKG;IACH,2BAA2B;QACvB,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACvD,CAAC;IAAA,CAAC;IAEF;;;;OAIG;IACH,eAAe;QACX,OAAO,IAAI,CAAC,aAAa,CAAC;IAC9B,CAAC;IAED;;;;OAIG;IACH,WAAW;QACP,OAAO,IAAI,CAAC,SAAS,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACH,gBAAgB;QACZ,OAAO,IAAI,CAAC,cAAc,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,YAAY;QACR,OAAO,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,aAAa;QACT,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACH,eAAe;QACX,OAAO,IAAI,CAAC,aAAa,CAAC;IAC9B,CAAC;IAED;;OAEG;IACH,wBAAwB;QACpB,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,aAAa,EAAE;YACxC,OAAO,GAAG,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,aAAa,GAAG,CAAC;SACxD;aAAM,IAAI,IAAI,CAAC,aAAa,EAAE;YAC3B,OAAO,IAAI,CAAC,aAAa,CAAC;SAC7B;aAAM;YACH,OAAO,IAAI,CAAC,UAAU,CAAA;SACzB;IACL,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,QAAQ;QACJ,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACH,YAAY;QACR,OAAO,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IAED,MAAM;QACF,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,OAAO;QACH,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;IAED,SAAS;QACL,OAAO,IAAI,CAAC,QAAQ,CAAC;IACzB,CAAC;IAED,YAAY;QACR,OAAO,IAAI,CAAC,MAAM,KAAK,8BAA8B,CAAC;IAC1D,CAAC;IAED;;;OAGG;IACH,gBAAgB;QACZ,oCAAoC;QACpC,OAAO,IAAI,CAAC,cAAc,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACH,mBAAmB;QACf,OAAO,IAAI,CAAC,iBAAiB,CAAC;IAClC,CAAC;IAED;;;OAGG;IACH,mBAAmB;QACf,OAAO,IAAI,CAAC,iBAAiB,CAAC;IAClC,CAAC;IAED;;;OAGG;IACH,aAAa;QACT,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACH,aAAa;QACT,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8DG;IACH,iBAAiB;QACb,OAAO,IAAI,CAAC,eAAe,CAAC;IAChC,CAAC;IAED;;;;OAIG;IACH,aAAa;QACT,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED,QAAQ;QACJ,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;IAED,mBAAmB;QACf,OAAO,IAAI,CAAC,iBAAiB,CAAC;IAClC,CAAC;IAED;;;;;OAKG;IACH,WAAW,CAAC,OAAe;QACvB,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,EAAE;YAC/B,OAAO,OAAO,CAAC;SAClB;QACD,IAAI,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,GAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;QAChD,IAAI,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,GAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;QAChD,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,OAAO,GAAG,OAAO,GAAG,EAAE,CAAC;QAEvB,IAAI,KAAK,GAAG,CAAC,EAAE;YACX,MAAM,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC;SACtD;QAED,MAAM,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC,OAAO,GAAI,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACrG,OAAO,MAAM,CAAC;IAClB,CAAC;IAAA,CAAC;CACL;AAED,OAAO,EAAE,IAAI,EAAE,CAAC;AAEhB,eAAe,IAAI,CAAC"}
--------------------------------------------------------------------------------
/build/Message.d.ts:
--------------------------------------------------------------------------------
1 | export declare type MessageData = {
2 | accountId: number;
3 | contactUri: string;
4 | fromUri: string;
5 | toUri: string;
6 | body: string;
7 | contentType: string;
8 | };
9 | /**
10 | * This class describes the information and current status of a call.
11 | */
12 | export default class Message {
13 | _accountId: number;
14 | _contactUri: string;
15 | _fromUri: string;
16 | _fromName: string;
17 | _fromNumber: string;
18 | _toUri: string;
19 | _body: string;
20 | _contentType: string;
21 | constructor({ accountId, contactUri, fromUri, toUri, body, contentType }: MessageData);
22 | /**
23 | * The account ID where this message belongs.
24 | * @returns {int}
25 | */
26 | getAccountId(): number;
27 | /**
28 | * The Contact URI of the sender, if present.
29 | * @returns {String}
30 | */
31 | getContactUri(): string;
32 | /**
33 | * URI of the sender.
34 | * @returns {String}
35 | */
36 | getFromUri(): string;
37 | /**
38 | * Sender name, or NULL if no name specified in URI.
39 | * @returns {String}
40 | */
41 | getFromName(): string;
42 | /**
43 | * Sender number
44 | * @returns {String}
45 | */
46 | getFromNumber(): string;
47 | /**
48 | * URI of the destination message.
49 | * @returns {String}
50 | */
51 | getToUri(): string;
52 | /**
53 | * Message body, or NULL if no message body is attached to this mesage.
54 | * @returns {String}
55 | */
56 | getBody(): string;
57 | /**
58 | * MIME type of the message.
59 | * @returns {String}
60 | */
61 | getContentType(): string;
62 | }
63 |
--------------------------------------------------------------------------------
/build/Message.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This class describes the information and current status of a call.
3 | */
4 | export default class Message {
5 | constructor({ accountId, contactUri, fromUri, toUri, body, contentType }) {
6 | let fromNumber = null;
7 | let fromName = null;
8 | if (fromUri) {
9 | let match = fromUri.match(/"([^"]+)" ;
2 | export default View;
3 |
--------------------------------------------------------------------------------
/build/PreviewVideoView.js:
--------------------------------------------------------------------------------
1 | import { requireNativeComponent } from 'react-native';
2 | import PropTypes from 'prop-types';
3 | const PreviewVideoView = {
4 | name: 'PjSipPreviewVideoView',
5 | propTypes: {
6 | deviceId: PropTypes.number.isRequired,
7 | objectFit: PropTypes.oneOf(['contain', 'cover'])
8 | },
9 | };
10 | const View = requireNativeComponent('PjSipPreviewVideoView');
11 | export default View;
12 | //# sourceMappingURL=PreviewVideoView.js.map
--------------------------------------------------------------------------------
/build/PreviewVideoView.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"PreviewVideoView.js","sourceRoot":"","sources":["../src/PreviewVideoView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,sBAAsB,EAAC,MAAM,cAAc,CAAC;AACvF,OAAO,SAAS,MAAM,YAAY,CAAC;AAEnC,MAAM,gBAAgB,GAAG;IACvB,IAAI,EAAE,uBAAuB;IAC7B,SAAS,EAAE;QACT,QAAQ,EAAE,SAAS,CAAC,MAAM,CAAC,UAAU;QACxC,SAAS,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;KAC9C;CACF,CAAC;AAEF,MAAM,IAAI,GAAG,sBAAsB,CAAC,uBAAuB,CAAC,CAAC;AAE7D,eAAe,IAAI,CAAC"}
--------------------------------------------------------------------------------
/build/RemoteVideoView.d.ts:
--------------------------------------------------------------------------------
1 | declare const View: import("react-native").HostComponent;
2 | export default View;
3 |
--------------------------------------------------------------------------------
/build/RemoteVideoView.js:
--------------------------------------------------------------------------------
1 | import { requireNativeComponent } from 'react-native';
2 | import PropTypes from 'prop-types';
3 | const RemoteVideoView = {
4 | name: 'PjSipRemoteVideoView',
5 | propTypes: {
6 | windowId: PropTypes.string.isRequired,
7 | objectFit: PropTypes.oneOf(['contain', 'cover'])
8 | },
9 | };
10 | const View = requireNativeComponent('PjSipRemoteVideoView');
11 | export default View;
12 | //# sourceMappingURL=RemoteVideoView.js.map
--------------------------------------------------------------------------------
/build/RemoteVideoView.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"RemoteVideoView.js","sourceRoot":"","sources":["../src/RemoteVideoView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,sBAAsB,EAAC,MAAM,cAAc,CAAC;AACvF,OAAO,SAAS,MAAM,YAAY,CAAC;AAEnC,MAAM,eAAe,GAAG;IACtB,IAAI,EAAE,sBAAsB;IAC5B,SAAS,EAAE;QACV,QAAQ,EAAE,SAAS,CAAC,MAAM,CAAC,UAAU;QACvC,SAAS,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;KAC9C;CACF,CAAC;AAEF,MAAM,IAAI,GAAG,sBAAsB,CAAC,sBAAsB,CAAC,CAAC;AAE5D,eAAe,IAAI,CAAC"}
--------------------------------------------------------------------------------
/build/index.d.ts:
--------------------------------------------------------------------------------
1 | import { default as Account } from './Account';
2 | import { default as Call } from './Call';
3 | import { default as Endpoint } from './Endpoint';
4 | export * from './Account';
5 | export * from './Call';
6 | export * from './Endpoint';
7 | export * from './PreviewVideoView';
8 | export * from './RemoteVideoView';
9 | export { Account, Call, Endpoint, };
10 | declare const _default: {
11 | Account: typeof Account;
12 | Call: typeof Call;
13 | Endpoint: typeof Endpoint;
14 | PreviewVideoView: import("react-native").HostComponent;
15 | RemoteVideoView: import("react-native").HostComponent;
16 | };
17 | export default _default;
18 |
--------------------------------------------------------------------------------
/build/index.js:
--------------------------------------------------------------------------------
1 | import { default as Account } from './Account';
2 | import { default as Call } from './Call';
3 | import { default as Endpoint } from './Endpoint';
4 | import { default as PreviewVideoView } from './PreviewVideoView';
5 | import { default as RemoteVideoView } from './RemoteVideoView';
6 | export * from './Account';
7 | export * from './Call';
8 | export * from './Endpoint';
9 | export * from './PreviewVideoView';
10 | export * from './RemoteVideoView';
11 | export { Account, Call, Endpoint, };
12 | export default {
13 | Account,
14 | Call,
15 | Endpoint,
16 | PreviewVideoView,
17 | RemoteVideoView,
18 | };
19 | //# sourceMappingURL=index.js.map
--------------------------------------------------------------------------------
/build/index.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAE/D,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAElC,OAAO,EACL,OAAO,EACP,IAAI,EACJ,QAAQ,GAGT,CAAA;AAED,eAAe;IACb,OAAO;IACP,IAAI;IACJ,QAAQ;IACR,gBAAgB;IAChB,eAAe;CAChB,CAAA"}
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Bold-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-Bold-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Bold-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-Bold-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-BoldItalic-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-BoldItalic-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-BoldItalic-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-BoldItalic-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Italic-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-Italic-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Italic-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-Italic-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Light-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-Light-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Light-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-Light-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-LightItalic-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-LightItalic-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-LightItalic-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-LightItalic-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Regular-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-Regular-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Regular-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-Regular-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Semibold-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-Semibold-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Semibold-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-Semibold-webfont.ttf
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-Semibold-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-Semibold-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-SemiboldItalic-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-SemiboldItalic-webfont.eot
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf
--------------------------------------------------------------------------------
/docs/fonts/OpenSans-SemiboldItalic-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/docs/fonts/OpenSans-SemiboldItalic-webfont.woff
--------------------------------------------------------------------------------
/docs/scripts/linenumber.js:
--------------------------------------------------------------------------------
1 | /*global document */
2 | (function() {
3 | var source = document.getElementsByClassName('prettyprint source linenums');
4 | var i = 0;
5 | var lineNumber = 0;
6 | var lineId;
7 | var lines;
8 | var totalLines;
9 | var anchorHash;
10 |
11 | if (source && source[0]) {
12 | anchorHash = document.location.hash.substring(1);
13 | lines = source[0].getElementsByTagName('li');
14 | totalLines = lines.length;
15 |
16 | for (; i < totalLines; i++) {
17 | lineNumber++;
18 | lineId = 'line' + lineNumber;
19 | lines[i].id = lineId;
20 | if (lineId === anchorHash) {
21 | lines[i].className += ' selected';
22 | }
23 | }
24 | }
25 | })();
26 |
--------------------------------------------------------------------------------
/docs/scripts/prettify/lang-css.js:
--------------------------------------------------------------------------------
1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com",
2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);
3 |
--------------------------------------------------------------------------------
/docs/styles/prettify-jsdoc.css:
--------------------------------------------------------------------------------
1 | /* JSDoc prettify.js theme */
2 |
3 | /* plain text */
4 | .pln {
5 | color: #000000;
6 | font-weight: normal;
7 | font-style: normal;
8 | }
9 |
10 | /* string content */
11 | .str {
12 | color: hsl(104, 100%, 24%);
13 | font-weight: normal;
14 | font-style: normal;
15 | }
16 |
17 | /* a keyword */
18 | .kwd {
19 | color: #000000;
20 | font-weight: bold;
21 | font-style: normal;
22 | }
23 |
24 | /* a comment */
25 | .com {
26 | font-weight: normal;
27 | font-style: italic;
28 | }
29 |
30 | /* a type name */
31 | .typ {
32 | color: #000000;
33 | font-weight: normal;
34 | font-style: normal;
35 | }
36 |
37 | /* a literal value */
38 | .lit {
39 | color: #006400;
40 | font-weight: normal;
41 | font-style: normal;
42 | }
43 |
44 | /* punctuation */
45 | .pun {
46 | color: #000000;
47 | font-weight: bold;
48 | font-style: normal;
49 | }
50 |
51 | /* lisp open bracket */
52 | .opn {
53 | color: #000000;
54 | font-weight: bold;
55 | font-style: normal;
56 | }
57 |
58 | /* lisp close bracket */
59 | .clo {
60 | color: #000000;
61 | font-weight: bold;
62 | font-style: normal;
63 | }
64 |
65 | /* a markup tag name */
66 | .tag {
67 | color: #006400;
68 | font-weight: normal;
69 | font-style: normal;
70 | }
71 |
72 | /* a markup attribute name */
73 | .atn {
74 | color: #006400;
75 | font-weight: normal;
76 | font-style: normal;
77 | }
78 |
79 | /* a markup attribute value */
80 | .atv {
81 | color: #006400;
82 | font-weight: normal;
83 | font-style: normal;
84 | }
85 |
86 | /* a declaration */
87 | .dec {
88 | color: #000000;
89 | font-weight: bold;
90 | font-style: normal;
91 | }
92 |
93 | /* a variable name */
94 | .var {
95 | color: #000000;
96 | font-weight: normal;
97 | font-style: normal;
98 | }
99 |
100 | /* a function name */
101 | .fun {
102 | color: #000000;
103 | font-weight: bold;
104 | font-style: normal;
105 | }
106 |
107 | /* Specify class=linenums on a pre to get line numbering */
108 | ol.linenums {
109 | margin-top: 0;
110 | margin-bottom: 0;
111 | }
112 |
--------------------------------------------------------------------------------
/docs/styles/prettify-tomorrow.css:
--------------------------------------------------------------------------------
1 | /* Tomorrow Theme */
2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */
3 | /* Pretty printing styles. Used with prettify.js. */
4 | /* SPAN elements with the classes below are added by prettyprint. */
5 | /* plain text */
6 | .pln {
7 | color: #4d4d4c; }
8 |
9 | @media screen {
10 | /* string content */
11 | .str {
12 | color: hsl(104, 100%, 24%); }
13 |
14 | /* a keyword */
15 | .kwd {
16 | color: hsl(240, 100%, 50%); }
17 |
18 | /* a comment */
19 | .com {
20 | color: hsl(0, 0%, 60%); }
21 |
22 | /* a type name */
23 | .typ {
24 | color: hsl(240, 100%, 32%); }
25 |
26 | /* a literal value */
27 | .lit {
28 | color: hsl(240, 100%, 40%); }
29 |
30 | /* punctuation */
31 | .pun {
32 | color: #000000; }
33 |
34 | /* lisp open bracket */
35 | .opn {
36 | color: #000000; }
37 |
38 | /* lisp close bracket */
39 | .clo {
40 | color: #000000; }
41 |
42 | /* a markup tag name */
43 | .tag {
44 | color: #c82829; }
45 |
46 | /* a markup attribute name */
47 | .atn {
48 | color: #f5871f; }
49 |
50 | /* a markup attribute value */
51 | .atv {
52 | color: #3e999f; }
53 |
54 | /* a declaration */
55 | .dec {
56 | color: #f5871f; }
57 |
58 | /* a variable name */
59 | .var {
60 | color: #c82829; }
61 |
62 | /* a function name */
63 | .fun {
64 | color: #4271ae; } }
65 | /* Use higher contrast and text-weight for printable form. */
66 | @media print, projection {
67 | .str {
68 | color: #060; }
69 |
70 | .kwd {
71 | color: #006;
72 | font-weight: bold; }
73 |
74 | .com {
75 | color: #600;
76 | font-style: italic; }
77 |
78 | .typ {
79 | color: #404;
80 | font-weight: bold; }
81 |
82 | .lit {
83 | color: #044; }
84 |
85 | .pun, .opn, .clo {
86 | color: #440; }
87 |
88 | .tag {
89 | color: #006;
90 | font-weight: bold; }
91 |
92 | .atn {
93 | color: #404; }
94 |
95 | .atv {
96 | color: #060; } }
97 | /* Style */
98 | /*
99 | pre.prettyprint {
100 | background: white;
101 | font-family: Consolas, Monaco, 'Andale Mono', monospace;
102 | font-size: 12px;
103 | line-height: 1.5;
104 | border: 1px solid #ccc;
105 | padding: 10px; }
106 | */
107 |
108 | /* Specify class=linenums on a pre to get line numbering */
109 | ol.linenums {
110 | margin-top: 0;
111 | margin-bottom: 0; }
112 |
113 | /* IE indents via margin-left */
114 | li.L0,
115 | li.L1,
116 | li.L2,
117 | li.L3,
118 | li.L4,
119 | li.L5,
120 | li.L6,
121 | li.L7,
122 | li.L8,
123 | li.L9 {
124 | /* */ }
125 |
126 | /* Alternate shading for lines */
127 | li.L1,
128 | li.L3,
129 | li.L5,
130 | li.L7,
131 | li.L9 {
132 | /* */ }
133 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import Account from './src/Account';
2 | import Call from './src/Call';
3 | import Endpoint from './src/Endpoint';
4 | import PreviewVideoView from './src/PreviewVideoView';
5 | import RemoteVideoView from './src/RemoteVideoView';
6 |
7 | module.exports = {
8 | Account,
9 | Call,
10 | Endpoint,
11 | PreviewVideoView,
12 | RemoteVideoView
13 | }
14 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea
3 |
--------------------------------------------------------------------------------
/ios/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea
3 |
--------------------------------------------------------------------------------
/ios/RTCPjSip.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/RTCPjSip.xcodeproj/project.xcworkspace/xcshareddata/RTCPjSip.xcscmblueprint:
--------------------------------------------------------------------------------
1 | {
2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "76DFC5D234DBB7DF07CA8A4A7F8D04D050533479",
3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
4 |
5 | },
6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
7 | "5841BB3ACF6C8ED064EDBC4A19CC0FD643E22C6E" : 0,
8 | "76DFC5D234DBB7DF07CA8A4A7F8D04D050533479" : 0
9 | },
10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "3F760B6C-DFE0-4541-B95D-65003559AFAA",
11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
12 | "5841BB3ACF6C8ED064EDBC4A19CC0FD643E22C6E" : "..",
13 | "76DFC5D234DBB7DF07CA8A4A7F8D04D050533479" : "react-native-pjsip\/"
14 | },
15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "RTCPjSip",
16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204,
17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "ios\/RTCPjSip.xcodeproj",
18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
19 | {
20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "http:\/\/git.carusto.com\/development\/carusto-mobile.git",
21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "5841BB3ACF6C8ED064EDBC4A19CC0FD643E22C6E"
23 | },
24 | {
25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/datso\/react-native-pjsip.git",
26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "76DFC5D234DBB7DF07CA8A4A7F8D04D050533479"
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/ios/RTCPjSip.xcodeproj/project.xcworkspace/xcuserdata/datso.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/ios/RTCPjSip.xcodeproj/project.xcworkspace/xcuserdata/datso.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/ios/RTCPjSip.xcodeproj/project.xcworkspace/xcuserdata/datso.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ios/RTCPjSip.xcodeproj/project.xcworkspace/xcuserdata/vadim.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/ios/RTCPjSip.xcodeproj/project.xcworkspace/xcuserdata/vadim.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/ios/RTCPjSip.xcodeproj/project.xcworkspace/xcuserdata/vadim.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ios/RTCPjSip.xcodeproj/project.xcworkspace/xcuserdata/vruban.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/ios/RTCPjSip.xcodeproj/project.xcworkspace/xcuserdata/vruban.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/ios/RTCPjSip.xcodeproj/xcuserdata/datso.xcuserdatad/xcschemes/RTCPjSip.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/ios/RTCPjSip.xcodeproj/xcuserdata/datso.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | RTCPjSip.xcscheme
8 |
9 | orderHint
10 | 1
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | DDFF87511D79D6F50096A8BE
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ios/RTCPjSip.xcodeproj/xcuserdata/vadim.xcuserdatad/xcschemes/RTCPjSip.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/ios/RTCPjSip.xcodeproj/xcuserdata/vadim.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | RTCPjSip.xcscheme
8 |
9 | orderHint
10 | 2
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | DDFF87511D79D6F50096A8BE
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ios/RTCPjSip.xcodeproj/xcuserdata/vruban.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | RTCPjSip.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipAccount.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "PjSipCall.h"
4 |
5 | @interface PjSipAccount : NSObject
6 |
7 | @property int id;
8 | @property NSString * name;
9 | @property NSString * username;
10 | @property NSString * domain;
11 | @property NSString * password;
12 | @property NSString * proxy;
13 | @property NSString * transport;
14 |
15 | @property NSString * contactParams;
16 | @property NSString * contactUriParams;
17 |
18 |
19 | @property NSString * regServer;
20 | @property NSNumber * regTimeout;
21 | @property NSDictionary * regHeaders;
22 | @property NSString * regContactParams;
23 | @property bool regOnAdd;
24 |
25 | + (instancetype)itemConfig:(NSDictionary *)config;
26 |
27 | - (id)initWithConfig:(NSDictionary *)config;
28 | - (int)id;
29 |
30 | - (PjSipCall *) makeCall: (NSString *) destination;
31 | - (void) register: (bool) renew;
32 |
33 | - (NSDictionary *)toJsonDictionary;
34 |
35 | @end
36 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipAccount.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #import "PjSipEndpoint.h"
5 | #import "PjSipAccount.h"
6 | #import "PjSipUtil.h"
7 |
8 |
9 | @implementation PjSipAccount
10 |
11 | + (instancetype)itemConfig:(NSDictionary *)config {
12 | return [[self alloc] initWithConfig:config];
13 | }
14 |
15 | - (id)initWithConfig:(NSDictionary *)config {
16 | self = [super init];
17 |
18 | if (self) {
19 | self.name = config[@"name"] == nil ? [NSNull null] : config[@"name"];
20 | self.username = config[@"username"];
21 | self.domain = config[@"domain"];
22 | self.password = config[@"password"];
23 |
24 | self.proxy = config[@"proxy"] == nil ? [NSNull null] : config[@"proxy"];
25 | self.transport = config[@"transport"] == nil ? [NSNull null] : config[@"transport"];
26 |
27 | self.contactParams = config[@"contactParams"] == nil ? [NSNull null] : config[@"contactParams"];
28 | self.contactUriParams = config[@"contactUriParams"] == nil ? [NSNull null] : config[@"contactUriParams"];
29 |
30 | self.regServer = config[@"regServer"] == nil ? [NSNull null] : config[@"regServer"];
31 | self.regTimeout = config[@"regTimeout"] == nil ? [NSNumber numberWithInteger:600] : config[@"regTimeout"];
32 | self.regHeaders = config[@"regHeaders"] == nil ? [NSNull null] : config[@"regHeaders"];
33 | self.regContactParams = config[@"regContactParams"] == nil ? [NSNull null] : config[@"regContactParams"];
34 | self.regOnAdd = [config[@"regOnAdd"] isEqual: @YES] || config[@"regOnAdd"] == nil ? true : false;
35 |
36 | pj_status_t status;
37 |
38 | pjsua_acc_config cfg;
39 | pjsua_acc_config_default(&cfg);
40 |
41 | cfg.vid_in_auto_show = PJ_TRUE;
42 | cfg.vid_out_auto_transmit = PJ_TRUE;
43 | cfg.ka_interval = 30;
44 |
45 | // General settings
46 | {
47 | NSString *cfgId;
48 | NSString *cfgURI = [NSString stringWithFormat:@"sip:%@", self.domain];
49 |
50 | if (![PjSipUtil isEmptyString:self.name]) {
51 | cfgId = [NSString stringWithFormat:@"%@ ", self.name, self.username, self.domain];
52 | } else {
53 | cfgId = [NSString stringWithFormat:@"", self.username, self.domain];
54 | }
55 |
56 | cfg.id = pj_str((char *) [cfgId UTF8String]);
57 | cfg.reg_uri = pj_str((char *) [cfgURI UTF8String]);
58 |
59 | pjsip_cred_info cred;
60 | cred.scheme = pj_str("digest");
61 | cred.realm = ![PjSipUtil isEmptyString:self.regServer] ? pj_str((char *) [self.regServer UTF8String]) : pj_str("*");
62 | cred.username = pj_str((char *) [self.username UTF8String]);
63 | cred.data = pj_str((char *) [self.password UTF8String]);
64 | cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
65 |
66 | cfg.cred_count = 1;
67 | cfg.cred_info[0] = cred;
68 |
69 | if (![PjSipUtil isEmptyString:self.contactParams]) {
70 | cfg.contact_params = pj_str((char *) [self.contactParams UTF8String]);
71 | }
72 | if (![PjSipUtil isEmptyString:self.contactUriParams]) {
73 | cfg.contact_uri_params = pj_str((char *) [self.contactUriParams UTF8String]);
74 | }
75 | }
76 |
77 | // Registration settings
78 | {
79 | if (![self.regHeaders isKindOfClass:[NSNull class]]) {
80 | pj_list_init(&cfg.reg_hdr_list);
81 |
82 | for(NSString* key in self.regHeaders) {
83 | struct pjsip_generic_string_hdr hdr;
84 | pj_str_t name = pj_str((char *) [key UTF8String]);
85 | pj_str_t value = pj_str((char *) [[self.regHeaders objectForKey:key] UTF8String]);
86 | pjsip_generic_string_hdr_init2(&hdr, &name, &value);
87 | pj_list_push_back(&cfg.reg_hdr_list, &hdr);
88 | }
89 | }
90 |
91 | if (![PjSipUtil isEmptyString:self.regContactParams]) {
92 | cfg.reg_contact_params = pj_str((char *) [self.regContactParams UTF8String]);
93 | }
94 |
95 | if (self.regTimeout != nil && ![self.regTimeout isKindOfClass:[NSNull class]]) {
96 | cfg.reg_timeout = (unsigned) [self.regTimeout intValue];
97 | }
98 |
99 | cfg.register_on_acc_add = self.regOnAdd;
100 | }
101 |
102 | // Transport settings
103 | {
104 | if (![PjSipUtil isEmptyString:self.proxy]) {
105 | cfg.proxy_cnt = 1;
106 | cfg.proxy[0] = pj_str((char *) [[NSString stringWithFormat:@"%@", self.proxy] UTF8String]);
107 | }
108 |
109 | cfg.transport_id = [[PjSipEndpoint instance] tcpTransportId];
110 |
111 | if (![PjSipUtil isEmptyString:self.transport] && ![self.transport isEqualToString:@"TCP"]) {
112 | if ([self.transport isEqualToString:@"UDP"]) {
113 | cfg.transport_id = [[PjSipEndpoint instance] udpTransportId];
114 | } else if ([self.transport isEqualToString:@"TLS"]) {
115 | cfg.transport_id = [[PjSipEndpoint instance] tlsTransportId];
116 | } else {
117 | NSLog(@"Illegal \"%@\" transport (possible values are UDP, TCP or TLS) use TCP instead", self.transport);
118 | }
119 | }
120 | }
121 |
122 | pjsua_acc_id account_id;
123 |
124 | status = pjsua_acc_add(&cfg, PJ_TRUE, &account_id);
125 | if (status != PJ_SUCCESS) {
126 | [NSException raise:@"Failed to create account" format:@"See device logs for more details."];
127 | }
128 |
129 | self.id = account_id;
130 | }
131 |
132 | return self;
133 | }
134 |
135 | - (void) dealloc {
136 | pjsua_acc_set_registration(self.id, PJ_FALSE);
137 | pjsua_acc_del(self.id);
138 | }
139 |
140 | - (void) register: (bool) renew {
141 | pj_status_t status = pjsua_acc_set_registration((pjsua_acc_id)self.id, renew ? PJ_TRUE : PJ_FALSE);
142 | if (status != PJ_SUCCESS) {
143 | [NSException raise:@"Failed to register account" format:@"See device logs for more details."];
144 | }
145 | }
146 |
147 | #pragma mark EventHandlers
148 |
149 | - (void)onRegistrationChanged {
150 | [[[PjSipEndpoint instance].bridge eventDispatcher] sendAppEventWithName:@"pjSipRegistrationChanged" body:[self toJsonDictionary]];
151 | }
152 |
153 | #pragma mark -
154 |
155 |
156 | - (NSDictionary *)toJsonDictionary {
157 | pjsua_acc_info info;
158 | pjsua_acc_get_info(self.id, &info);
159 |
160 | // Format registration status
161 | NSDictionary * registration = @{
162 | @"status": [PjSipUtil toString:(pj_str_t *) pjsip_get_status_text(info.status)],
163 | @"statusText": [PjSipUtil toString:&info.status_text],
164 | @"active": [PjSipUtil isActive:&info.expires],
165 | @"reason": @"test"
166 | };
167 |
168 | return @{
169 | @"id": @(self.id),
170 | @"uri": [PjSipUtil toString:&info.acc_uri],
171 | @"name": self.name,
172 | @"username": self.username,
173 | @"domain": self.domain,
174 | @"password": self.password,
175 | @"proxy": self.proxy,
176 | @"transport": self.transport,
177 | @"contactParams": self.contactParams,
178 | @"contactUriParams": self.contactUriParams,
179 | @"regServer": self.regServer,
180 | @"regTimeout": self.regTimeout,
181 | @"regContactParams": self.regContactParams,
182 | @"regHeaders": self.regHeaders,
183 |
184 | @"registration": registration
185 | };
186 | }
187 |
188 | @end
189 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipCall.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface PjSipCall : NSObject
5 |
6 | @property int id;
7 | @property bool isHeld;
8 | @property bool isMuted;
9 |
10 | + (instancetype)itemConfig:(int)id;
11 |
12 | - (void)hangup;
13 | - (void)decline;
14 | - (void)answer;
15 | - (void)hold;
16 | - (void)unhold;
17 | - (void)mute;
18 | - (void)unmute;
19 | - (void)xfer:(NSString*) destination;
20 | - (void)xferReplaces:(int) destinationCallId;
21 | - (void)redirect:(NSString*) destination;
22 | - (void)dtmf:(NSString*) digits;
23 |
24 | - (void)onStateChanged:(pjsua_call_info) callInfo;
25 | - (void)onMediaStateChanged:(pjsua_call_info) callInfo;
26 |
27 | - (NSDictionary *)toJsonDictionary:(bool) isSpeaker;
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipCall.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #import "PjSipCall.h"
5 | #import "PjSipUtil.h"
6 |
7 | @implementation PjSipCall
8 |
9 | + (instancetype)itemConfig:(int)id {
10 | return [[self alloc] initWithId:id];
11 | }
12 |
13 | - (id)initWithId:(int)id {
14 | self = [super init];
15 |
16 | if (self) {
17 | self.id = id;
18 | self.isHeld = false;
19 | self.isMuted = false;
20 | }
21 |
22 | return self;
23 | }
24 |
25 | #pragma mark - Actions
26 |
27 | - (void) hangup {
28 | pj_status_t status = pjsua_call_hangup(self.id, 0, NULL, NULL);
29 |
30 | if (status != PJ_SUCCESS) {
31 | NSLog(@"Failed to hangup a call (%d)", status);
32 | }
33 | }
34 |
35 | - (void) decline {
36 | pjsua_call_hangup(self.id, PJSIP_SC_DECLINE, NULL, NULL);
37 | }
38 |
39 |
40 | - (void)answer {
41 | // TODO: Add parameters to answer with
42 | // TODO: Put on hold previous call
43 |
44 | pjsua_msg_data msgData;
45 | pjsua_msg_data_init(&msgData);
46 | pjsua_call_setting callOpt;
47 | pjsua_call_setting_default(&callOpt);
48 |
49 | // TODO: Audio/Video count configuration!
50 | callOpt.aud_cnt = 1;
51 | callOpt.vid_cnt = 1;
52 |
53 | pjsua_call_answer2(self.id, &callOpt, 200, NULL, &msgData);
54 | }
55 |
56 | - (void)hold {
57 | if (self.isHeld) {
58 | return;
59 | }
60 |
61 | self.isHeld = true;
62 |
63 | // TODO: May be check whether call is answered before putting on hold
64 | pjsua_call_set_hold(self.id, NULL);
65 | }
66 |
67 | - (void)unhold {
68 | if (!self.isHeld) {
69 | return;
70 | }
71 |
72 | self.isHeld = false;
73 |
74 | // TODO: May be check whether call is answered before releasing from hold
75 | pjsua_call_reinvite(self.id, PJSUA_CALL_UNHOLD, NULL);
76 | }
77 |
78 | - (void)mute {
79 | pjsua_call_info info;
80 | pjsua_call_get_info(self.id, &info);
81 |
82 | @try {
83 | if( info.conf_slot != 0 ) {
84 | NSLog(@"WC_SIPServer microphone disconnected from call");
85 | pjsua_conf_disconnect(0, info.conf_slot);
86 |
87 | self.isMuted = true;
88 | }
89 | }
90 | @catch (NSException *exception) {
91 | NSLog(@"Unable to mute microphone: %@", exception);
92 | }
93 | }
94 |
95 | - (void)unmute {
96 | pjsua_call_info info;
97 | pjsua_call_get_info(self.id, &info);
98 |
99 | @try {
100 | if( info.conf_slot != 0 ) {
101 | NSLog(@"WC_SIPServer microphone reconnected to call");
102 | pjsua_conf_connect(0, info.conf_slot);
103 |
104 | self.isMuted = false;
105 | }
106 | }
107 | @catch (NSException *exception) {
108 | NSLog(@"Unable to un-mute microphone: %@", exception);
109 | }
110 | }
111 |
112 | - (void)xfer:(NSString*) destination {
113 | pj_str_t value = pj_str((char *) [destination UTF8String]);
114 | pjsua_call_xfer(self.id, &value, NULL);
115 | }
116 |
117 | - (void)xferReplaces:(int) destinationCallId {
118 | pjsua_call_xfer_replaces(self.id, destinationCallId, 0, NULL);
119 | }
120 |
121 | - (void)redirect:(NSString*) destination {
122 | pjsua_msg_data msgData;
123 | pjsip_generic_string_hdr my_hdr;
124 | pj_str_t hname = pj_str("Contact");
125 | pj_str_t hvalue = pj_str((char *) [destination UTF8String]);
126 | pjsua_msg_data_init(&msgData);
127 | pjsip_generic_string_hdr_init2(&my_hdr, &hname, &hvalue);
128 | pj_list_push_back(&msgData.hdr_list, &my_hdr);
129 |
130 | pjsua_call_setting callOpt;
131 | pjsua_call_setting_default(&callOpt);
132 | pjsua_call_answer2(self.id, &callOpt, PJSIP_SC_MOVED_TEMPORARILY, NULL, &msgData);
133 | }
134 |
135 | - (void)dtmf:(NSString*) digits {
136 | // TODO: Fallback for "The RFC 2833 payload format did not work".
137 |
138 | pj_str_t value = pj_str((char *) [digits UTF8String]);
139 | pjsua_call_dial_dtmf(self.id, &value);
140 | }
141 |
142 | #pragma mark - Callback methods
143 |
144 | - (void)onStateChanged:(pjsua_call_info)info {
145 | // Ignore
146 | }
147 |
148 | /**
149 | * The action may connect the call to sound device, to file, or
150 | * to loop the call.
151 | */
152 | - (void)onMediaStateChanged:(pjsua_call_info)info {
153 | pjsua_call_media_status status = info.media_status;
154 |
155 | if (status == PJSUA_CALL_MEDIA_ACTIVE || status == PJSUA_CALL_MEDIA_REMOTE_HOLD) {
156 | pjsua_conf_connect(info.conf_slot, 0);
157 | pjsua_conf_connect(0, info.conf_slot);
158 | }
159 | }
160 |
161 | #pragma mark - Extra
162 |
163 | - (NSDictionary *)toJsonDictionary:(bool) isSpeaker {
164 | pjsua_call_info info;
165 | pjsua_call_get_info(self.id, &info);
166 |
167 | // -----
168 | int connectDuration = -1;
169 |
170 | if (info.state == PJSIP_INV_STATE_CONFIRMED ||
171 | info.state == PJSIP_INV_STATE_DISCONNECTED) {
172 | connectDuration = info.connect_duration.sec;
173 | }
174 |
175 | return @{
176 | @"id": @(self.id),
177 | @"callId": [PjSipUtil toString:&info.call_id],
178 | @"accountId": @(info.acc_id),
179 |
180 | @"localContact": [PjSipUtil toString:&info.local_contact],
181 | @"localUri": [PjSipUtil toString:&info.local_info],
182 | @"remoteContact": [PjSipUtil toString:&info.remote_contact],
183 | @"remoteUri": [PjSipUtil toString:&info.remote_info],
184 | @"state": [PjSipUtil callStateToString:info.state],
185 | @"stateText": [PjSipUtil toString:&info.state_text],
186 | @"connectDuration": @(connectDuration),
187 | @"totalDuration": @(info.total_duration.sec),
188 |
189 | @"lastStatusCode": [PjSipUtil callStatusToString:info.last_status],
190 | @"lastReason": [PjSipUtil toString:&info.last_status_text],
191 |
192 | @"held": @(self.isHeld),
193 | @"muted": @(self.isMuted),
194 | @"speaker": @(isSpeaker),
195 |
196 | @"remoteOfferer": @(info.rem_offerer),
197 | @"remoteAudioCount": @(info.rem_aud_cnt),
198 | @"remoteVideoCount": @(info.rem_vid_cnt),
199 |
200 | @"audioCount": @(info.setting.aud_cnt),
201 | @"videoCount": @(info.setting.vid_cnt),
202 |
203 | @"media": [self mediaInfoToJsonArray:info.media count:info.media_cnt],
204 | @"provisionalMedia": [self mediaInfoToJsonArray:info.prov_media count:info.prov_media_cnt]
205 | };
206 | }
207 |
208 | - (NSArray *)mediaInfoToJsonArray: (pjsua_call_media_info[]) info count:(int) count {
209 | NSMutableArray * result = [NSMutableArray array];
210 |
211 | for (int i = 0; i < count; i++) {
212 | [result addObject:[self mediaToJsonDictionary:info[i]]];
213 | }
214 |
215 | return result;
216 | }
217 |
218 | - (NSDictionary *)mediaToJsonDictionary:(pjsua_call_media_info) info {
219 | return @{
220 | @"dir": [PjSipUtil mediaDirToString:info.dir],
221 | @"type": [PjSipUtil mediaTypeToString:info.type],
222 | @"status": [PjSipUtil mediaStatusToString:info.status],
223 | @"audioStream": @{
224 | @"confSlot": @(info.stream.aud.conf_slot)
225 | },
226 | @"videoStream": @{
227 | @"captureDevice": @(info.stream.vid.cap_dev),
228 | @"windowId": @(info.stream.vid.win_in),
229 | }
230 | };
231 | }
232 |
233 | @end
234 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipEndpoint.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "PjSipAccount.h"
4 | #import "PjSipCall.h"
5 |
6 |
7 | @interface PjSipEndpoint : NSObject
8 |
9 | @property NSMutableDictionary* accounts;
10 | @property NSMutableDictionary* calls;
11 | @property(nonatomic, strong) RCTBridge *bridge;
12 |
13 | @property pjsua_transport_id tcpTransportId;
14 | @property pjsua_transport_id udpTransportId;
15 | @property pjsua_transport_id tlsTransportId;
16 |
17 | @property bool isSpeaker;
18 |
19 | +(instancetype)instance;
20 |
21 | -(NSDictionary *)start: (NSDictionary *) config;
22 | -(NSDictionary *)stop: (NSDictionary *) config;
23 |
24 | -(void) updateStunServers: (int) accountId stunServerList:(NSArray *)stunServerList;
25 |
26 | -(PjSipAccount *)createAccount:(NSDictionary*) config;
27 | -(void) deleteAccount:(int) accountId;
28 | -(NSMutableArray *)getAccounts;
29 | -(PjSipAccount *)findAccount:(int)accountId;
30 | -(PjSipCall *)makeCall:(PjSipAccount *) account destination:(NSString *)destination callSettings: (NSDictionary *)callSettings msgData: (NSDictionary *)msgData;
31 | -(void)pauseParallelCalls:(PjSipCall*) call; // TODO: Remove this feature.
32 | -(PjSipCall *)findCall:(int)callId;
33 | -(void)useSpeaker;
34 | -(void)useEarpiece;
35 |
36 | -(void)changeOrientation: (NSString*) orientation;
37 | -(void)changeCodecSettings: (NSDictionary*) codecSettings;
38 |
39 | -(void)emmitRegistrationChanged:(PjSipAccount*) account;
40 | -(void)emmitCallReceived:(PjSipCall*) call;
41 | -(void)emmitCallUpdated:(PjSipCall*) call;
42 | -(void)emmitCallChanged:(PjSipCall*) call;
43 | -(void)emmitCallTerminated:(PjSipCall*) call;
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipLocalVideoViewManager.h:
--------------------------------------------------------------------------------
1 | //#import
2 | //#import
3 | //
4 | //@interface PjSipLocalVideoViewManager : RCTViewManager
5 | //@property *UIView videoView;
6 | //@end
7 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipMessage.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface PjSipMessage : NSObject
4 |
5 | @property NSDictionary * data;
6 |
7 | + (instancetype)itemConfig:(NSDictionary *)config;
8 | - (id)initWithConfig:(NSDictionary *)config;
9 |
10 | - (NSDictionary *)toJsonDictionary;
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipMessage.m:
--------------------------------------------------------------------------------
1 | #import "PjSipMessage.h"
2 |
3 | @implementation PjSipMessage
4 |
5 | + (instancetype)itemConfig:(NSDictionary *)config {
6 | return [[self alloc] initWithConfig:config];
7 | }
8 |
9 | - (id)initWithConfig:(NSDictionary *)config {
10 | self = [super init];
11 |
12 | if (self) {
13 | self.data = config;
14 | }
15 |
16 | return self;
17 | }
18 |
19 | - (NSDictionary *)toJsonDictionary {
20 | return self.data;
21 | }
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipModule.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface PjSipModule : NSObject
4 |
5 | @end
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipModule.m:
--------------------------------------------------------------------------------
1 | #import "PjSipEndpoint.h"
2 | #import "PjSipModule.h"
3 |
4 | #import
5 | #import
6 | #import
7 |
8 | @interface PjSipModule ()
9 |
10 | @end
11 |
12 | @implementation PjSipModule
13 |
14 | @synthesize bridge = _bridge;
15 |
16 | - (dispatch_queue_t)methodQueue {
17 | // TODO: Use special thread may be ?
18 | // return dispatch_queue_create("com.carusto.PJSipMdule", DISPATCH_QUEUE_SERIAL);
19 | return dispatch_get_main_queue();
20 | }
21 |
22 | - (instancetype)init {
23 | self = [super init];
24 | return self;
25 | }
26 |
27 | + (BOOL)requiresMainQueueSetup
28 | {
29 | return YES;
30 | }
31 |
32 | RCT_EXPORT_METHOD(start: (NSDictionary *) config callback: (RCTResponseSenderBlock) callback) {
33 | [PjSipEndpoint instance].bridge = self.bridge;
34 |
35 | NSDictionary *result = [[PjSipEndpoint instance] start: config];
36 | callback(@[@TRUE, result]);
37 | }
38 |
39 | // Stub method
40 | RCT_EXPORT_METHOD(stop: (RCTResponseSenderBlock) callback) {
41 | // [PjSipEndpoint instance].bridge = self.bridge;
42 |
43 | // [[PjSipEndpoint instance] stop: config];
44 | callback(@[@TRUE]);
45 | }
46 |
47 | RCT_EXPORT_METHOD(updateStunServers: (int) accountId stunServerList:(NSArray *) stunServerList callback:(RCTResponseSenderBlock) callback) {
48 | [[PjSipEndpoint instance] updateStunServers:accountId stunServerList:stunServerList];
49 | callback(@[@TRUE]);
50 | }
51 |
52 | #pragma mark - Account Actions
53 |
54 | RCT_EXPORT_METHOD(createAccount: (NSDictionary *) config callback:(RCTResponseSenderBlock) callback) {
55 | PjSipAccount *account = [[PjSipEndpoint instance] createAccount:config];
56 | callback(@[@TRUE, [account toJsonDictionary]]);
57 | }
58 |
59 | RCT_EXPORT_METHOD(deleteAccount: (int) accountId callback:(RCTResponseSenderBlock) callback) {
60 | [[PjSipEndpoint instance] deleteAccount:accountId];
61 | callback(@[@TRUE]);
62 | }
63 |
64 | RCT_EXPORT_METHOD(getAccount: (int) accountId callback:(RCTResponseSenderBlock) callback) {
65 | @try {
66 | PjSipAccount *account = [[PjSipEndpoint instance] findAccount:accountId];
67 | callback(@[@TRUE, [account toJsonDictionary]]);
68 | }
69 | @catch (NSException * e) {
70 | callback(@[@FALSE, e.reason]);
71 | }
72 | }
73 |
74 | RCT_EXPORT_METHOD(getAccounts: callback: (RCTResponseSenderBlock) callback) {
75 | NSMutableArray *result = [[PjSipEndpoint instance] getAccounts];
76 | callback(@[@TRUE, result]);
77 | }
78 |
79 | RCT_EXPORT_METHOD(registerAccount: (int) accountId renew:(BOOL) renew callback:(RCTResponseSenderBlock) callback) {
80 | @try {
81 | PjSipEndpoint* endpoint = [PjSipEndpoint instance];
82 | PjSipAccount *account = [endpoint findAccount:accountId];
83 |
84 | [account register:renew];
85 |
86 | callback(@[@TRUE]);
87 | }
88 | @catch (NSException * e) {
89 | callback(@[@FALSE, e.reason]);
90 | }
91 | }
92 |
93 | #pragma mark - Call Actions
94 |
95 | RCT_EXPORT_METHOD(makeCall: (int) accountId destination: (NSString *) destination callSettings:(NSDictionary*) callSettings msgData:(NSDictionary*) msgData callback:(RCTResponseSenderBlock) callback) {
96 | @try {
97 | PjSipEndpoint* endpoint = [PjSipEndpoint instance];
98 | PjSipAccount *account = [endpoint findAccount:accountId];
99 | PjSipCall *call = [endpoint makeCall:account destination:destination callSettings:callSettings msgData:msgData];
100 |
101 | // TODO: Remove this function
102 | // Automatically put other calls on hold.
103 | [endpoint pauseParallelCalls:call];
104 |
105 | callback(@[@TRUE, [call toJsonDictionary:endpoint.isSpeaker]]);
106 | }
107 | @catch (NSException * e) {
108 | callback(@[@FALSE, e.reason]);
109 | }
110 | }
111 |
112 | RCT_EXPORT_METHOD(hangupCall: (int) callId callback:(RCTResponseSenderBlock) callback) {
113 | PjSipCall *call = [[PjSipEndpoint instance] findCall:callId];
114 |
115 | if (call) {
116 | [call hangup];
117 | callback(@[@TRUE]);
118 | } else {
119 | callback(@[@FALSE, @"Call not found"]);
120 | }
121 | }
122 |
123 | RCT_EXPORT_METHOD(declineCall: (int) callId callback:(RCTResponseSenderBlock) callback) {
124 | PjSipCall *call = [[PjSipEndpoint instance] findCall:callId];
125 |
126 | if (call) {
127 | [call decline];
128 | callback(@[@TRUE]);
129 | } else {
130 | callback(@[@FALSE, @"Call not found"]);
131 | }
132 | }
133 |
134 | RCT_EXPORT_METHOD(answerCall: (int) callId callback:(RCTResponseSenderBlock) callback) {
135 | PjSipEndpoint* endpoint = [PjSipEndpoint instance];
136 | PjSipCall *call = [endpoint findCall:callId];
137 |
138 | if (call) {
139 | [call answer];
140 |
141 | // Automatically put other calls on hold.
142 | [endpoint pauseParallelCalls:call];
143 |
144 | callback(@[@TRUE]);
145 | } else {
146 | callback(@[@FALSE, @"Call not found"]);
147 | }
148 | }
149 |
150 | RCT_EXPORT_METHOD(holdCall: (int) callId callback:(RCTResponseSenderBlock) callback) {
151 | PjSipEndpoint* endpoint = [PjSipEndpoint instance];
152 | PjSipCall *call = [endpoint findCall:callId];
153 |
154 | if (call) {
155 | [call hold];
156 | [endpoint emmitCallChanged:call];
157 |
158 | callback(@[@TRUE]);
159 | } else {
160 | callback(@[@FALSE, @"Call not found"]);
161 | }
162 | }
163 |
164 | RCT_EXPORT_METHOD(unholdCall: (int) callId callback:(RCTResponseSenderBlock) callback) {
165 | PjSipEndpoint* endpoint = [PjSipEndpoint instance];
166 | PjSipCall *call = [endpoint findCall:callId];
167 |
168 | if (call) {
169 | [call unhold];
170 | [endpoint emmitCallChanged:call];
171 |
172 | // Automatically put other calls on hold.
173 | [endpoint pauseParallelCalls:call];
174 |
175 | callback(@[@TRUE]);
176 | } else {
177 | callback(@[@FALSE, @"Call not found"]);
178 | }
179 | }
180 |
181 | RCT_EXPORT_METHOD(muteCall: (int) callId callback:(RCTResponseSenderBlock) callback) {
182 | PjSipEndpoint* endpoint = [PjSipEndpoint instance];
183 | PjSipCall *call = [endpoint findCall:callId];
184 |
185 | if (call) {
186 | [call mute];
187 | [endpoint emmitCallChanged:call];
188 | callback(@[@TRUE]);
189 | } else {
190 | callback(@[@FALSE, @"Call not found"]);
191 | }
192 | }
193 |
194 | RCT_EXPORT_METHOD(unMuteCall: (int) callId callback:(RCTResponseSenderBlock) callback) {
195 | PjSipEndpoint* endpoint = [PjSipEndpoint instance];
196 | PjSipCall *call = [endpoint findCall:callId];
197 |
198 | if (call) {
199 | [call unmute];
200 | [endpoint emmitCallChanged:call];
201 | callback(@[@TRUE]);
202 | } else {
203 | callback(@[@FALSE, @"Call not found"]);
204 | }
205 | }
206 |
207 | RCT_EXPORT_METHOD(xferCall: (int) callId destination: (NSString *) destination callback:(RCTResponseSenderBlock) callback) {
208 | PjSipCall *call = [[PjSipEndpoint instance] findCall:callId];
209 |
210 | if (call) {
211 | [call xfer:destination];
212 | callback(@[@TRUE]);
213 | } else {
214 | callback(@[@FALSE, @"Call not found"]);
215 | }
216 | }
217 |
218 | RCT_EXPORT_METHOD(xferReplacesCall: (int) callId destinationCallId: (int) destinationCallId callback:(RCTResponseSenderBlock) callback) {
219 | PjSipCall *call = [[PjSipEndpoint instance] findCall:callId];
220 |
221 | if (call) {
222 | [call xferReplaces:destinationCallId];
223 | callback(@[@TRUE]);
224 | } else {
225 | callback(@[@FALSE, @"Call not found"]);
226 | }
227 | }
228 |
229 | RCT_EXPORT_METHOD(redirectCall: (int) callId destination: (NSString *) destination callback:(RCTResponseSenderBlock) callback) {
230 | PjSipCall *call = [[PjSipEndpoint instance] findCall:callId];
231 |
232 | if (call) {
233 | [call redirect:destination];
234 | callback(@[@TRUE]);
235 | } else {
236 | callback(@[@FALSE, @"Call not found"]);
237 | }
238 | }
239 |
240 | RCT_EXPORT_METHOD(dtmfCall: (int) callId digits: (NSString *) digits callback:(RCTResponseSenderBlock) callback) {
241 | PjSipCall *call = [[PjSipEndpoint instance] findCall:callId];
242 |
243 | if (call) {
244 | [call dtmf:digits];
245 | callback(@[@TRUE]);
246 | } else {
247 | callback(@[@FALSE, @"Call not found"]);
248 | }
249 | }
250 |
251 | RCT_EXPORT_METHOD(useSpeaker: (RCTResponseSenderBlock) callback) {
252 | [[PjSipEndpoint instance] useSpeaker];
253 | }
254 |
255 | RCT_EXPORT_METHOD(useEarpiece: (RCTResponseSenderBlock) callback) {
256 | [[PjSipEndpoint instance] useEarpiece];
257 | }
258 |
259 | RCT_EXPORT_METHOD(activateAudioSession: (RCTResponseSenderBlock) callback) {
260 | pjsua_set_no_snd_dev();
261 | pj_status_t status;
262 | status = pjsua_set_snd_dev(PJMEDIA_AUD_DEFAULT_CAPTURE_DEV, PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV);
263 | if (status != PJ_SUCCESS) {
264 | NSLog(@"Failed to active audio session");
265 | }
266 | callback(@[@TRUE]);
267 | }
268 |
269 | RCT_EXPORT_METHOD(deactivateAudioSession: (RCTResponseSenderBlock) callback) {
270 | @try {
271 | NSLog(@"TELEPHONY TRACE: Deactivate audio session");
272 | pjsua_set_no_snd_dev();
273 | NSLog(@"TELEPHONY TRACE: Before callback");
274 | callback(@[@TRUE]);
275 | NSLog(@"TELEPHONY TRACE: After callback");
276 | }
277 | @catch (NSException * e) {
278 | NSLog(@"TELEPHONY TRACE: Error!");
279 | callback(@[@FALSE, e.reason]);
280 | }
281 | }
282 |
283 | #pragma mark - Settings
284 |
285 | RCT_EXPORT_METHOD(changeOrientation: (NSString*) orientation) {
286 | [[PjSipEndpoint instance] changeOrientation:orientation];
287 | }
288 |
289 | RCT_EXPORT_METHOD(changeCodecSettings: (NSDictionary*) codecSettings callback:(RCTResponseSenderBlock) callback) {
290 | [[PjSipEndpoint instance] changeCodecSettings:codecSettings];
291 | callback(@[@TRUE]);
292 | }
293 |
294 | RCT_EXPORT_MODULE();
295 |
296 | @end
297 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipPreviewVideoViewManager.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface PjSipPreviewVideoViewManager : RCTViewManager
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipPreviewVideoViewManager.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 | #import
5 | #import "PjSipVideo.h"
6 | #import "PjSipPreviewVideoViewManager.h"
7 |
8 | @implementation PjSipPreviewVideoViewManager
9 |
10 | RCT_EXPORT_MODULE()
11 |
12 | -(UIView *) view {
13 | return [[PjSipVideo alloc] init];
14 | }
15 |
16 | - (dispatch_queue_t) methodQueue {
17 | return dispatch_get_main_queue();
18 | }
19 |
20 | RCT_CUSTOM_VIEW_PROPERTY(deviceId, NSNumber, PjSipVideo) {
21 | pjmedia_vid_dev_index device = [[RCTConvert NSNumber:json] intValue];
22 | pjsua_vid_preview_param param;
23 | pjsua_vid_preview_param_default(¶m);
24 |
25 | pj_status_t status = pjsua_vid_preview_start(device, ¶m);
26 |
27 | if (status == PJ_SUCCESS) {
28 | pjsua_vid_win_id winId = pjsua_vid_preview_get_win(device);
29 | [view setWindowId:winId];
30 | } else {
31 | NSLog(@"Failed to pjsua_vid_win_get_info %@ (%@ error code)", @(device), @(status));
32 | [view setWindowId:-1];
33 | }
34 | }
35 |
36 | RCT_CUSTOM_VIEW_PROPERTY(objectFit, NSString, PjSipVideo) {
37 | ObjectFit fit = contain;
38 |
39 | if ([[RCTConvert NSString:json] isEqualToString:@"cover"]) {
40 | fit = cover;
41 | }
42 |
43 | [view setObjectFit:fit];
44 | }
45 |
46 | @end
47 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipRemoteVideoViewManager.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface PjSipRemoteVideoViewManager : RCTViewManager
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipRemoteVideoViewManager.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 | #import
5 | #import "PjSipUtil.h"
6 | #import "PjSipVideo.h"
7 | #import "PjSipRemoteVideoViewManager.h"
8 |
9 | @implementation PjSipRemoteVideoViewManager
10 |
11 | RCT_EXPORT_MODULE()
12 |
13 | -(UIView *) view {
14 | return [[PjSipVideo alloc] init];
15 | }
16 |
17 | - (dispatch_queue_t) methodQueue {
18 | return dispatch_get_main_queue();
19 | }
20 |
21 | RCT_CUSTOM_VIEW_PROPERTY(windowId, NSString, PjSipVideo) {
22 | pjsua_vid_win_id windowId = [[RCTConvert NSNumber:json] intValue];
23 | [view setWindowId:windowId];
24 | }
25 |
26 | RCT_CUSTOM_VIEW_PROPERTY(objectFit, NSString, PjSipVideo) {
27 | ObjectFit fit = contain;
28 |
29 | if ([[RCTConvert NSString:json] isEqualToString:@"cover"]) {
30 | fit = cover;
31 | }
32 |
33 | [view setObjectFit:fit];
34 | }
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipUtil.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface PjSipUtil : NSObject
5 |
6 | +(NSString *) toString: (pj_str_t *) str;
7 | +(NSNumber *) isActive: (unsigned *) expires;
8 | +(BOOL) isEmptyString : (NSString *) str;
9 |
10 | +(NSString *) callStateToString: (pjsip_inv_state) state;
11 | +(NSString *) callStatusToString: (pjsip_status_code) status;
12 | +(NSString *) mediaDirToString: (pjmedia_dir) dir;
13 | +(NSString *) mediaStatusToString: (pjsua_call_media_status) status;
14 | +(NSString *) mediaTypeToString: (pjmedia_type) type;
15 |
16 | +(void) fillCallSettings: (pjsua_call_setting*) callSettings dict:(NSDictionary*) dict;
17 | +(void) fillMsgData: (pjsua_msg_data*) msgData dict:(NSDictionary*) dict pool:(pj_pool_t*) pool;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipVideo.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 | #import
5 |
6 | // TODO: Add ability to change device orientation!
7 |
8 | typedef enum ObjectFit : NSUInteger {
9 | contain,
10 | cover
11 | } ObjectFit;
12 |
13 | @interface PjSipVideo : UIView
14 | @property pjsua_vid_win_id winId;
15 | @property UIView* winView;
16 | @property ObjectFit winFit;
17 |
18 | -(void)setWindowId:(pjsua_vid_win_id) windowId;
19 | -(void)setObjectFit:(ObjectFit) objectFit;
20 | @end
21 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipVideo.m:
--------------------------------------------------------------------------------
1 | #import "PjSipVideo.h"
2 |
3 | @implementation PjSipVideo {
4 |
5 | }
6 |
7 | - (id) init {
8 | if (self = [super init]) {
9 | self.winId = -1;
10 | self.winFit = cover;
11 | self.winView = NULL;
12 | }
13 |
14 | [[NSNotificationCenter defaultCenter] addObserver:self
15 | selector:@selector(setNeedsLayout:)
16 | name:@"PjSipInvalidateVideo"
17 | object:nil];
18 |
19 | return self;
20 | }
21 |
22 | -(void) setNeedsLayout:(NSNotification *)notification {
23 | [self dispatchAsyncSetNeedsLayout];
24 | }
25 |
26 | - (void) setWindowId:(pjsua_vid_win_id) windowId {
27 | if (self.winId == windowId) {
28 | return;
29 | }
30 |
31 | self.winId = windowId;
32 |
33 | if (self.winView != NULL) {
34 | [self.winView removeFromSuperview];
35 | self.winView = NULL;
36 | }
37 |
38 | if (windowId >= 0) {
39 | pjsua_vid_win_info wi;
40 | pj_status_t status = pjsua_vid_win_get_info(windowId, &wi);
41 |
42 | if (status == PJ_SUCCESS) {
43 | self.winView = (__bridge UIView *) wi.hwnd.info.ios.window;
44 | [self addSubview:self.winView];
45 | }
46 | }
47 |
48 | [self dispatchAsyncSetNeedsLayout];
49 | }
50 |
51 | -(void)setObjectFit:(ObjectFit) objectFit {
52 | if (self.winFit == objectFit) {
53 | return;
54 | }
55 |
56 | self.winFit = objectFit;
57 |
58 | [self dispatchAsyncSetNeedsLayout];
59 | }
60 |
61 |
62 | - (void)layoutSubviews
63 | {
64 | [super layoutSubviews];
65 |
66 | UIView *subview = self.winView;
67 | if (!subview) {
68 | return;
69 | }
70 |
71 | pjsua_vid_win_info wi;
72 | pj_status_t status = pjsua_vid_win_get_info(self.winId, &wi);
73 |
74 | if (status != PJ_SUCCESS) {
75 | return;
76 | }
77 |
78 | CGFloat width = wi.size.w, height = wi.size.h;
79 |
80 | CGRect newValue;
81 | if (width <= 0 || height <= 0) {
82 | newValue.origin.x = 0;
83 | newValue.origin.y = 0;
84 | newValue.size.width = 0;
85 | newValue.size.height = 0;
86 | } else if (self.winFit == cover) {
87 | newValue = self.bounds;
88 |
89 | if (newValue.size.width != width || newValue.size.height != height) {
90 | CGFloat scaleFactor = MAX(newValue.size.width / width, newValue.size.height / height);
91 |
92 | // Scale both width and height in order to make it obvious that the aspect
93 | // ratio is preserved.
94 | width *= scaleFactor;
95 | height *= scaleFactor;
96 | newValue.origin.x += (newValue.size.width - width) / 2.0;
97 | newValue.origin.y += (newValue.size.height - height) / 2.0;
98 | newValue.size.width = width;
99 | newValue.size.height = height;
100 | }
101 | } else {
102 | newValue = AVMakeRectWithAspectRatioInsideRect(CGSizeMake(width, height), self.bounds);
103 | }
104 |
105 | CGRect oldValue = subview.frame;
106 | if (newValue.origin.x != oldValue.origin.x
107 | || newValue.origin.y != oldValue.origin.y
108 | || newValue.size.width != oldValue.size.width
109 | || newValue.size.height != oldValue.size.height) {
110 | subview.frame = newValue;
111 | }
112 |
113 | subview.transform = CGAffineTransformIdentity;
114 | }
115 |
116 | /**
117 | * Invalidates the current layout of the receiver and triggers a layout update
118 | * during the next update cycle. Make sure that the method call is performed on
119 | * the application's main thread (as documented to be necessary by Apple).
120 | */
121 | - (void)dispatchAsyncSetNeedsLayout {
122 | __weak UIView *weakSelf = self;
123 | dispatch_async(dispatch_get_main_queue(), ^{
124 | UIView *strongSelf = weakSelf;
125 | [strongSelf setNeedsLayout];
126 | });
127 | }
128 |
129 | @end
130 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipVideoViewManager.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface PjSipVideoViewManager : RCTViewManager
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/ios/RTCPjSip/PjSipVideoViewManager.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import "PjSipVideoViewManager.m"
3 | #import
4 | #import
5 |
6 | /**
7 | * Implements an equivalent of {@code HTMLVideoElement} i.e. Web's video
8 | * element.
9 | */
10 | @interface RTCVideoView : RCTView
11 |
12 | // @property pjsua_vid_win_id vidWinId;
13 |
14 | @end
15 |
16 | @implementation RTCVideoView {
17 |
18 | }
19 |
20 | ///**
21 | // * Initializes and returns a newly allocated view object with the specified
22 | // * frame rectangle.
23 | // *
24 | // * @param frame The frame rectangle for the view, measured in points.
25 | // */
26 | //- (instancetype)init {
27 | // self = [super init];
28 | //
29 | // UITextView* textView = [[UITextView alloc] initWithFrame:self.bounds];
30 | // textView.text = @"Hi BRO!";
31 | // textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
32 | //
33 | //// UIView* textView = [[UIView alloc] initWithFrame:self.bounds];
34 | //// textView.backgroundColor = [UIColor redColor];
35 | //// textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
36 | //
37 | // [self addSubview:textView];
38 | //
39 | // return self;
40 | //}
41 |
42 | //- (instancetype)init
43 | //{
44 | // self = [super init];
45 | //
46 | //
47 | // UITextView *myTextView = [[UITextView alloc] initWithFrame:asd];
48 | // [self.view addSubview:myTextView];
49 | //
50 | // UITextView* textView = [[UITextView alloc] init];
51 | // textView.text = @"Hi BRO!";
52 | //
53 | // [self addSubview:textView];
54 | //
55 | // return self;
56 | //}
57 |
58 |
59 | @end
60 |
61 | @interface PjSipVideoViewManager : RCTViewManager
62 |
63 | @end
64 |
65 |
66 | @implementation PjSipVideoViewManager
67 |
68 | RCT_EXPORT_MODULE()
69 |
70 | -(UIView *)view {
71 | return [[RTCVideoView alloc] init];
72 | }
73 |
74 | - (dispatch_queue_t)methodQueue {
75 | return dispatch_get_main_queue();
76 | }
77 |
78 | RCT_CUSTOM_VIEW_PROPERTY(windowId, NSNumber, RTCVideoView) {
79 |
80 | // NSLog(@"RCT_CUSTOM_VIEW_PROPERTY");
81 | //
82 | // UIView* textView = [[UIView alloc] initWithFrame:view.bounds];
83 | // textView.backgroundColor = [UIColor redColor];
84 | // textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
85 | //
86 | // [view addSubview:textView];
87 | //
88 | //
89 | // __weak UIView *weakSelf = view;
90 | // dispatch_async(dispatch_get_main_queue(), ^{
91 | // UIView *strongSelf = weakSelf;
92 | // [strongSelf setNeedsLayout];
93 | // });
94 |
95 |
96 | // TODO: Remove this
97 |
98 | NSLog(@"RCT_CUSTOM_VIEW_PROPERTY");
99 |
100 | pjsua_vid_win_id c[64];
101 | unsigned k, count = PJ_ARRAY_SIZE(c);
102 | pjsua_vid_enum_wins(c, &count);
103 |
104 | NSLog(@"RCT_CUSTOM_VIEW_PROPERTY Size: %d", count);
105 |
106 | for (NSUInteger i = 0; i < count; i++) {
107 | NSLog(@"Window Id: %d", c[i]);
108 | }
109 |
110 |
111 | // Start
112 | NSNumber *s = [RCTConvert NSNumber:json];
113 |
114 | int d = [s intValue], i, last;
115 | i = (d == PJSUA_INVALID_ID) ? 0 : d;
116 | last = (d == PJSUA_INVALID_ID) ? PJSUA_MAX_VID_WINS : d+1;
117 |
118 |
119 |
120 | for (;i < last; ++i) {
121 | pjsua_vid_win_info wi;
122 |
123 | if (pjsua_vid_win_get_info(i, &wi) == PJ_SUCCESS) {
124 | UIView *parent = view;
125 | UIView *videoView = (__bridge UIView *)wi.hwnd.info.ios.window;
126 | videoView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
127 |
128 | if (videoView && ![videoView isDescendantOfView:parent]) {
129 | CGRect newFrame = videoView.frame;
130 | newFrame.size.width = wi.size.w;
131 | newFrame.size.height = wi.size.h;
132 |
133 | [videoView setFrame:view.bounds];
134 | [view addSubview:videoView];
135 |
136 |
137 | NSLog(@"Add video to view!!!");
138 |
139 |
140 | //
141 | //
142 | //
143 | //
144 | // dispatch_async(dispatch_get_main_queue(), ^{
145 | // UIView *strongSelf = weakSelf;
146 | //
147 | // /* Add the video window as subview */
148 | // // [strongSelf addSubview:videoView];
149 | //
150 | //// CGRect newFrame = videoView.frame;
151 | //// newFrame.size.width = wi.size.w;
152 | //// newFrame.size.height = wi.size.h;
153 | //
154 | //// [videoView setFrame:newFrame];
155 | //
156 | //
157 | // UITextView* textView = [[UITextView alloc] initWithFrame:strongSelf.bounds];
158 | // textView.text = @"Hi BRO!";
159 | // textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
160 | //
161 | // [view addSubview:textView];
162 | //
163 | // NSLog(@"Add subview!!");
164 | //
165 | //// if (!wi.is_native) {
166 | //// /* Center it horizontally */
167 | //// videoView.center = CGPointMake(parent.bounds.size.width/2.0,
168 | //// videoView.bounds.size.height/2.0);
169 | //// } else {
170 | //// /* Preview window, move it to the bottom */
171 | //// videoView.center = CGPointMake(parent.bounds.size.width/2.0,
172 | //// parent.bounds.size.height-
173 | //// videoView.bounds.size.height/2.0);
174 | //// }
175 | // [strongSelf setNeedsLayout];
176 | // });
177 | }
178 | }
179 | }
180 |
181 |
182 |
183 | // RTCVideoTrack *videoTrack;
184 | //
185 | // if (json) {
186 | // NSString *streamId = (NSString *)json;
187 | //
188 | // WebRTCModule *module = [self.bridge moduleForName:@"WebRTCModule"];
189 | // RTCMediaStream *stream = module.mediaStreams[streamId];
190 | // NSArray *videoTracks = stream.videoTracks;
191 | //
192 | // videoTrack = videoTracks.count ? videoTracks[0] : nil;
193 | // } else {
194 | // videoTrack = nil;
195 | // }
196 | //
197 | // view.videoTrack = videoTrack;
198 | }
199 |
200 |
201 | @end
202 |
--------------------------------------------------------------------------------
/libs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | DIR="$( cd "$( dirname "$0" )" && pwd )"
4 |
5 | VERSION="v2.9.0"
6 | URL="https://github.com/mcjambi/react-native-pjsip-builder/archive/refs/heads/main.zip"
7 | # URL="https://github.com/mcjambi/react-native-pjsip-builder/archive/refs/tags/${VERSION}.tar.gz"
8 | LOCK="${DIR}/.libs.lock"
9 | DEST=".libs.tar.gz"
10 | DOWNLOAD=true
11 |
12 | if ! type "wget" > /dev/null; then
13 | echo "Missed wget dependency" >&2;
14 | exit 1;
15 | fi
16 | if ! type "tar" > /dev/null; then
17 | echo "Missed tar dependency" >&2;
18 | exit 1;
19 | fi
20 |
21 | if ! type "unzip" > /dev/null; then
22 | echo "Missed unzip dependency" >&2;
23 | exit 1;
24 | fi
25 |
26 | if ! type "git-lfs" > /dev/null; then
27 | echo "Missed git-lfs dependency" >&2;
28 | exit 1;
29 | fi
30 |
31 | if ! type "tsc" > /dev/null; then
32 | echo "Missed TYPESCRIPT dependency" >&2;
33 | exit 1;
34 | fi
35 |
36 |
37 | # Rebuild using typescript
38 | cd "${DIR}/"
39 | tsc
40 | cd ~
41 |
42 |
43 |
44 |
45 | if [ -f ${LOCK} ]; then
46 | CURRENT_VERSION=$(cat ${LOCK})
47 |
48 | if [ "${CURRENT_VERSION}" == "${VERSION}" ];then
49 | DOWNLOAD=false
50 | fi
51 | fi
52 |
53 | if [ "$DOWNLOAD" = true ]; then
54 | wget "${URL}" -O "${DEST}"
55 | tar -xvf "${DEST}"
56 | rm -f "${DEST}"
57 | echo "${VERSION}" > ${LOCK}
58 |
59 | echo "Make dist folder ..."
60 | mkdir -p "${DIR}/dist"
61 |
62 | echo "Go to dist folder ..."
63 | cd "${DIR}/dist"
64 |
65 | wget "https://github.com/VoIPGRID/Vialer-pjsip-iOS/archive/3.5.zip" -O "3.5.zip"
66 | wget "https://media.githubusercontent.com/media/VoIPGRID/Vialer-pjsip-iOS/3.5/VialerPJSIP.framework/Versions/A/VialerPJSIP" -O "VialerPJSIP"
67 | unzip "3.5.zip"
68 | unlink "3.5.zip"
69 |
70 | mkdir -p "${DIR}/ios/VialerPJSIP.framework"
71 |
72 | # mv -vn "${DIR}/dist/Vialer-pjsip-iOS-3.5/VialerPJSIP.framework/Versions/Current/*" "${DIR}/ios/VialerPJSIP.framework"
73 | cp -R "${DIR}/dist/Vialer-pjsip-iOS-3.5/VialerPJSIP.framework/Versions/Current/Headers" "${DIR}/ios/VialerPJSIP.framework"
74 | mv "${DIR}/dist/VialerPJSIP" "${DIR}/ios/VialerPJSIP.framework/VialerPJSIP"
75 |
76 | # then remove all dist
77 | rm -Rf "${DIR}/dist"
78 | fi
79 |
80 | if [ "$DOWNLOAD" = false ]; then
81 | CURRENT_VERSION=$(cat ${LOCK})
82 | echo "I have downloaded! Check it again or remove ${LOCK} file. ${CURRENT_VERSION}"
83 | fi
84 |
85 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "./build/index.js",
3 | "module": "./build/index.js",
4 | "types": "./build/index.d.ts",
5 | "scripts": {
6 | "postinstall": "bash libs.sh",
7 | "lint": "eslint ./src",
8 | "build": "tsc",
9 | "generate-docs": "node_modules/.bin/jsdoc --configure .jsdoc.json --verbose"
10 | },
11 | "author": {
12 | "name": "Jam Viet",
13 | "email": "mcjambi@gmail.com"
14 | },
15 | "license": "GPL-3.0",
16 | "bugs": {
17 | "url": "https://github.com/mcjambi/react-native-xsip/issues"
18 | },
19 | "dependencies": {
20 | "events": "1.1.0"
21 | },
22 | "devDependencies": {
23 | "@babel/cli": "7.13.0",
24 | "@babel/core": "7.13.8",
25 | "@babel/preset-typescript": "7.7.4",
26 | "@types/events": "3.0.0",
27 | "@types/react-native": "0.63.25",
28 | "@babel/eslint-parser": "^7.18.9",
29 | "eslint": "^5.6.0",
30 | "eslint-config-airbnb": "^17.1.0",
31 | "eslint-plugin-import": "^2.14.0",
32 | "eslint-plugin-jsx-a11y": "^6.1.1",
33 | "eslint-plugin-react": "^7.11.1",
34 | "eslint-plugin-react-native": "^3.3.0",
35 | "gh-pages": "^2.0.0",
36 | "jsdoc": "^3.5.5",
37 | "minami": "^1.2.3",
38 | "typescript": "4.0.3"
39 | },
40 | "name": "react-native-xsip",
41 | "description": "PJSIP module for React Native",
42 | "homepage": "https://github.com/mcjambi/react-native-xsip",
43 | "keywords": [
44 | "react-component",
45 | "react-native",
46 | "ios",
47 | "android",
48 | "pjsip",
49 | "sip"
50 | ],
51 | "nativePackage": true,
52 | "optionalDependencies": {},
53 | "peerDependencies": {
54 | "react-native": ">=0.64.0"
55 | },
56 | "repository": {
57 | "type": "git",
58 | "url": "git+https://github.com/mcjambi/react-native-xsip.git"
59 | },
60 | "version": "2.10.15"
61 | }
62 |
--------------------------------------------------------------------------------
/react-native-xsip.podspec:
--------------------------------------------------------------------------------
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 = package['name']
7 | s.version = package['version']
8 | s.summary = package['description']
9 | s.license = package['license']
10 |
11 | s.authors = package['author']
12 | s.homepage = package['homepage']
13 | s.platform = :ios, "9.0"
14 |
15 | s.source = { :git => "https://github.com/mcjambi/react-native-xsip.git" }
16 | s.source_files = "ios/**/*.{h,m}"
17 |
18 | s.dependency 'React'
19 | s.vendored_frameworks = 'ios/VialerPJSIP.framework'
20 | s.xcconfig = {
21 | 'GCC_PREPROCESSOR_DEFINITIONS' => 'PJ_AUTOCONF=1',
22 | 'USE_HEADERMAP' => 'NO',
23 | }
24 | end
25 |
--------------------------------------------------------------------------------
/src/Account.ts:
--------------------------------------------------------------------------------
1 | import AccountRegistration from './AccountRegistration'
2 |
3 | export type AccountConfiguration = {
4 | id: number,
5 | uri: string,
6 | name: string,
7 | username: string,
8 | domain: string | null,
9 | password: string,
10 | proxy: string,
11 | transport: string,
12 | contactParams: string,
13 | contactUriParams: string,
14 | regServer: string,
15 | regTimeout: string,
16 | regContactParams: string,
17 | regHeaders: Object
18 | };
19 |
20 | /**
21 | * This describes account configuration and registration status
22 | */
23 | export default class Account {
24 | _data: AccountConfiguration;
25 | _registration: AccountRegistration;
26 |
27 | constructor(data: any) {
28 | this._data = data;
29 | this._registration = new AccountRegistration(data['registration']);
30 | }
31 |
32 | /**
33 | * The account ID.
34 | * @returns {int}
35 | */
36 | getId(): number {
37 | return this._data.id;
38 | }
39 |
40 | /**
41 | * This is the URL to be put in the request URI for the registration, and will look something like "sip:serviceprovider".
42 | * @returns {String}
43 | */
44 | getURI(): string {
45 | return this._data.uri;
46 | }
47 |
48 | /**
49 | * Full name specified in Endpoint.createAccount().
50 | * @returns {String}
51 | */
52 | getName(): string {
53 | return this._data.name;
54 | }
55 |
56 | /**
57 | * Username specified in Endpoint.createAccount().
58 | * @returns {String}
59 | */
60 | getUsername(): string {
61 | return this._data.username;
62 | }
63 |
64 | /**
65 | * Domain specified in Endpoint.createAccount().
66 | * @returns {int|null}
67 | */
68 | getDomain(): string | null {
69 | return this._data.domain;
70 | }
71 |
72 | /**
73 | * Password specified in Endpoint.createAccount().
74 | * @returns {String}
75 | */
76 | getPassword(): string {
77 | return this._data.password;
78 | }
79 |
80 | /**
81 | * Proxy specified in Endpoint.createAccount().
82 | * @returns {String}
83 | */
84 | getProxy(): string {
85 | return this._data.proxy;
86 | }
87 |
88 | /**
89 | * Transport specified in Endpoint.createAccount().
90 | * @returns {String}
91 | */
92 | getTransport(): string {
93 | return this._data.transport;
94 | }
95 |
96 | /**
97 | * Additional parameters that will be appended in the Contact header
98 | * for this account.
99 | * @returns {String}
100 | */
101 | getContactParams(): string {
102 | return this._data.contactParams;
103 | }
104 |
105 | /**
106 | * Additional URI parameters that will be appended in the Contact URI
107 | * for this account.
108 | * @returns {String}
109 | */
110 | getContactUriParams(): string {
111 | return this._data.contactUriParams;
112 | }
113 |
114 | /**
115 | * Port specified in Endpoint.createAccount().
116 | * @returns {String}
117 | */
118 | getRegServer(): string {
119 | return this._data.regServer || "";
120 | }
121 |
122 | /**
123 | * Port specified in Endpoint.createAccount().
124 | * @returns {String}
125 | */
126 | getRegTimeout(): string {
127 | return this._data.regTimeout;
128 | }
129 |
130 | /**
131 | * @returns {String}
132 | */
133 | getRegContactParams(): string {
134 | return this._data.regContactParams;
135 | }
136 |
137 | /**
138 | * @returns {Object}
139 | */
140 | getRegHeaders(): Object {
141 | return this._data.regHeaders;
142 | }
143 |
144 | /**
145 | * Account registration status.
146 | * @returns {AccountRegistration}
147 | */
148 | getRegistration(): AccountRegistration {
149 | return this._registration;
150 | }
151 | }
--------------------------------------------------------------------------------
/src/AccountRegistration.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Account registration information. Application can query the registration info
3 | * by calling account.getRegistration().
4 | */
5 | export default class AccountRegistration {
6 | _status: string;
7 | _statusText: string;
8 | _active: boolean;
9 | _reason: string;
10 |
11 | constructor({status, statusText, active, reason}) {
12 | this._status = status;
13 | this._statusText = statusText;
14 | this._active = active;
15 | this._reason = reason;
16 | }
17 |
18 | /**
19 | * Last registration status code (SIP status codes according to RFC 3261).
20 | * If status code is empty, the account is currently not registered. Any other value indicates the SIP
21 | * status code of the registration.
22 | *
23 | * @returns {string|null}
24 | */
25 | getStatus(): string | null {
26 | return this._status;
27 | }
28 |
29 | /**
30 | * String describing the registration status.
31 | *
32 | * @returns {string|null}
33 | */
34 | getStatusText(): string | null {
35 | return this._statusText;
36 | }
37 |
38 | /**
39 | * Flag to tell whether this account is currently registered
40 | * (has active registration session).
41 | *
42 | * @returns boolean
43 | */
44 | isActive(): boolean {
45 | return this._active;
46 | }
47 |
48 | /**
49 | * Reason phrase received.
50 | *
51 | * @returns {String|null}
52 | */
53 | getReason(): string | null {
54 | return this._reason;
55 | }
56 |
57 | toJson(): {
58 | status: string,
59 | statusText: string,
60 | active: boolean,
61 | reason: string,
62 | } {
63 | return {
64 | status: this._status,
65 | statusText: this._statusText,
66 | active: this._active,
67 | reason: this._reason
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/src/Message.ts:
--------------------------------------------------------------------------------
1 | export type MessageData = {
2 | accountId: number,
3 | contactUri: string,
4 | fromUri: string,
5 | toUri: string,
6 | body: string,
7 | contentType: string,
8 | }
9 |
10 | /**
11 | * This class describes the information and current status of a call.
12 | */
13 | export default class Message {
14 |
15 | _accountId: number;
16 | _contactUri: string;
17 | _fromUri: string;
18 | _fromName: string;
19 | _fromNumber: string;
20 | _toUri: string;
21 | _body: string;
22 | _contentType: string;
23 |
24 | constructor({
25 | accountId,
26 | contactUri,
27 | fromUri,
28 | toUri,
29 | body,
30 | contentType
31 | }: MessageData) {
32 | let fromNumber = null;
33 | let fromName = null;
34 |
35 | if (fromUri) {
36 | let match = fromUri.match(/"([^"]+)" {
67 | console.log("Account: ", account);
68 |
69 | setTimeout(() => {
70 | endpoint.registerAccount(account, true);
71 | }, 10000);
72 |
73 | setTimeout(() => {
74 | endpoint.registerAccount(account, false);
75 | }, 20000);
76 | });
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/wiki/android_notification_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcjambi/react-native-xsip/444a61ecfc025e87422e44de0187c79ef8062dde/wiki/android_notification_example.png
--------------------------------------------------------------------------------
/wiki/android_sip_background.md:
--------------------------------------------------------------------------------
1 | # Android background service
2 |
3 | In order to accept incoming calls while applicaiton in background you should set `notifications` property to `true` (true by default).
4 | This will make PJSIP service run in the *foreground*, supplying the ongoing notification to be shown to the user while in this state.
5 | Without foreground notification, Android could kill PJSIP service to reclaim more memory.
6 |
7 | 
8 |
9 | ```javascript
10 | let configuration = {
11 | service: {
12 | ua: Platform.select({ios: "Reachify iOS", android: "Reachify Android"}), // Default: React Native PjSip (version)
13 | notifications: true, // Creates peding notification that will allow service work while your app in background
14 | notifications: false, // Disables pending notification
15 | notifications: {
16 | account: true,
17 | call: false // Disables only call notification
18 | },
19 | notifications: {
20 | account: {
21 | title: "My cool react native app", // Default: account name
22 | text: "Here we go", // Default: account registration status
23 | info: null,
24 | ticker: null,
25 | smallIcon: null,
26 | largeIcon: null
27 | },
28 | call: {
29 | title: "Active call", // Default: "Call in Progress - %Account Name%"
30 | text: "John Doe", // Default: "%Caller Name% (%Number%)"
31 | info: null,
32 | ticker: null, // Default: "Call in Progress"
33 | smallIcon: "icon_call", // Default: R.drawable.stat_sys_phone_call
34 | largeIcon: null
35 | }
36 | }
37 | },
38 | network: {
39 | useAnyway: false, // Default: true
40 | useWifi: true, // Default: true
41 | use3g: true, // Default: false
42 | useEdge: false, // Default: false
43 | useGprs: false, // Default: false
44 | useInRoaming: false, // Default: false
45 | useOtherNetworks: true // Default: false
46 | }
47 | };
48 | let endpoint = new Endpoint();
49 | let state = await endpoint.start(configuration);
50 | // ...
51 | ```
52 |
53 | ### smallIcon & largeIcon
54 | To use own images for nofitications, copy them into `android/app/src/main/res/mipmap-XXXX/` and set thier names into `smallIcon` and `largeIcon` without extension.
55 | For more info: [ui_guidelines/icon_design_status_bar](https://developer.android.com/guide/practices/ui_guidelines/icon_design_status_bar.html)
56 |
57 | ### Handle clicks to call notifications
58 |
59 | Typically you should contain code that will change "route" in react-native app depending on result of `endpoint.start` command
60 | ```javascript
61 | let state = await endpoint.start(configuration);
62 | let calls = state.calls; // A list of active calls
63 |
64 | if (state.hasOwnProperty("notificationCallId")) {
65 | for (let c of calls) {
66 | if (c.getId() == state['notificationCallId']) {
67 | route = {name:'call', call: c};
68 | break;
69 | }
70 | }
71 | }
72 |
73 | //...
74 |
75 | // If true you should use slider instead of buttons for incoming call, because device was in sleep when this call comes.
76 | if (state.notificationIsFromForeground) {
77 | //...
78 | }
79 | ```
80 |
--------------------------------------------------------------------------------
/wiki/calls.md:
--------------------------------------------------------------------------------
1 | TODO: Introduction + links to other sections.
2 |
3 | # Events
4 |
5 | All interaction from javascript to pjsip module is asynchronous.
6 | So for each action, promise will be returned.
7 |
8 | ## call_received
9 |
10 | TODO: Description
11 |
12 | ## call_changed
13 |
14 | TODO: Description
15 |
16 | ## call_terminated
17 |
18 | TODO: Description
19 |
20 |
21 | # Actions
22 |
23 | ## Initiate a call
24 | To be able to make a call first of all you should createAccount, and pass account instance into Endpoint.makeCall function.
25 | This function will return a promise that will be resolved when PjSIP initializes the call.
26 |
27 | ```
28 | let options = {
29 | headers: {
30 | "P-Assserted-Identity": "Header example",
31 | "X-UA": "React native"
32 | }
33 | }
34 |
35 | let call = await endpoint.makeCall(account, destination, options);
36 | call.getId() // Use this id to detect changes and make actions
37 |
38 | endpoint.addListener("call_changed", (newCall) => {
39 | if (call.getId() === newCall.getId()) {
40 | // Our call changed, do smth.
41 | }
42 | }
43 | endpoint.addListener("call_terminated", (newCall) => {
44 | if (call.getId() === newCall.getId()) {
45 | // Our call terminated
46 | }
47 | }
48 | ```
49 |
50 | ## Answer the call
51 |
52 | After answer there will be event "call_changed" that reflect the changes.
53 | If there is already active call, it will be placed on hold (so expect "call_changed" event)
54 |
55 | ```
56 | let options = {};
57 | let call = ...;
58 | let promise = endpoint.answerCall(call, options);
59 | promise.then(() => {
60 | // Answer complete, expect that "call_changed" will be fired.
61 | }));
62 |
63 | promise.catch(() => {
64 | // Answer failed, show error
65 | });
66 | ```
67 |
68 | ## Hangup
69 | Use this function when you have active call, and Decline for unanswered incoming calls.
70 | After successul hangup, Endpoint should fire "call_terminated" event, use it to how final call duration and status.
71 |
72 | ```
73 | let options = {};
74 | let call = ...;
75 | await endpoint.hangupCall(call, options);
76 | ```
77 |
78 | ## Decline
79 | Use this function when you have unanswered incoming call.
80 | After successul decline, Endpoint should fire "call_terminated" event.
81 |
82 | ```
83 | let options = {};
84 | let call = ...;
85 | await endpoint.declineCall(call, options);
86 | ```
87 |
88 | ## Hold/Unhold
89 |
90 | TODO: Description
91 | After successul hold/unhold, Endpoint should fire "call_changed" event, where `isHeld` should be false or true.
92 |
93 | ```
94 | let options = {};
95 | let call = ...;
96 |
97 | await endpoint.holdCall(call, options);
98 | await endpoint.unholdCall(call, options);
99 | ```
100 |
101 | ## Transfer
102 |
103 | TODO: Description
104 |
105 | ```
106 | let options = {};
107 | let call = ...;
108 |
109 | await endpoint.xferCall(call, destination, options);
110 | ```
111 |
112 | ## DTMF
113 |
114 | TODO: Description
115 |
116 | ```
117 | let options = {};
118 | let call = ...;
119 | let key = "3";
120 |
121 | await endpoint.dtmfCall(call, key, options);
122 | ```
123 |
--------------------------------------------------------------------------------
/wiki/installation_android.md:
--------------------------------------------------------------------------------
1 | # Android installation
2 |
3 | ## Step 1
4 | Add permissions & service to `android/app/src/main/AndroidManifest.xml`
5 |
6 | ```xml
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | ```
20 |
21 | ```xml
22 |
23 | ...
24 |
28 | ...
29 |
30 | ```
31 |
32 | ## Step 2
33 | ```bash
34 | react-native link
35 | ```
36 |
37 | ## Additional step: Ability to answer incoming call without Lock Screen
38 |
39 | In `android/app/src/main/java/com/xxx/MainActivity.java`
40 |
41 | ```java
42 | import android.view.Window;
43 | import android.view.WindowManager;
44 | import android.os.Bundle;
45 | ...
46 | @Override
47 | public void onCreate(Bundle savedInstanceState) {
48 | super.onCreate(savedInstanceState);
49 |
50 | Window w = getWindow();
51 | w.setFlags(
52 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
53 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
54 | );
55 | }
56 | ```
57 |
58 | ## If Android targetSdk >= 23
59 |
60 | If your Android targetSdk is 23 or above you should grant `android.permission.RECORD_AUDIO` at runtime before making/receiving an audio call.
61 |
62 | To check and request Android permissions, please check out [react-native-android-permissions](https://github.com/lucasferreira/react-native-android-permissions).
63 |
--------------------------------------------------------------------------------
/wiki/installation_ios.md:
--------------------------------------------------------------------------------
1 | # iOS installation
2 |
3 | ## Step 1
4 | Link library
5 |
6 | ```bash
7 | react-native link
8 | ```
9 |
10 | ## Step 2
11 | Open project in xcode.
12 | 1. In the project build settings, make sure you have enabled All settings to be visible.
13 | 2. The Build Options are the 4th section down. Select *No* for the Enable Bitcode option.
14 |
15 | ## Step 3
16 | Add permissions and capabilities to use microphone and camera by adding following lines to `ios/%PROJECT_NAME%/Info.plist`
17 |
18 | ```xml
19 | NSCameraUsageDescription
20 | Video calls
21 | NSMicrophoneUsageDescription
22 | Audio calls
23 | UIBackgroundModes
24 |
25 | audio
26 | fetch
27 | voip
28 |
29 | ```
30 |
31 | # PushNotifications
32 |
33 | To be able to receive incoming call when app in background you have to use PushKit.
34 | This is the only way to receive information and wake up application to perform some action like use CallKit to show incoming call dialog.
35 |
36 | ## How it works
37 | 1. Your application registers for receiving VoIP notifications
38 | 2. After a successful registration your application adds *device token* to Contact header when REGISTER.
39 | 3. Your SIP server should parse those headers and when someone calling to those user, server should also send push notification for those device(s).
40 | 4. When iOS application receives this PushNotificaiton it should show incoming call dialog via callkit, and register on server.
41 | 5. Server should send INVITE to new registration from this iOS device.
42 | 6. When user press Answer button via callkit, iOS application answers those SIP call.
43 |
44 | ## Client side
45 | When application starts, it should send REGISTER with additional attributes of `Contact` header and use a long term *registration timeout*.
46 | In example bellow we use one month as regTimeout to be sure that our registration will not be expired when application goes to background.
47 |
48 | Your configuration might looks like this
49 | ```javascript
50 | endpoint.createAccount({
51 | "username":"****",
52 | "domain":"****",
53 | "password":"****",
54 | "regTimeout": 2592000, // one month
55 | "regContactParams": ";app-id=****;pn-voip-tok=XXXXXXXXX;pn-im-tok=XXXXXXXXXX"
56 | })
57 | ```
58 |
59 | ## Server side
60 | Your SIP server should support ability to send PushNotifications and also have addtional logic that re-send's INVITE messages during calling to user when new registration is available.
61 | For example an working module for freeswitch consider using *mod_apn*.
62 |
63 | # CallKit
64 |
65 | Ensure that the Push Notification Capability is ON
66 | TODO: Example
--------------------------------------------------------------------------------
/wiki/ios_push_notifications_callkit.md:
--------------------------------------------------------------------------------
1 | # PushNotifications
2 |
3 | To be able to receive incoming call when app in background you have to use PushKit.
4 | This is the only way to receive information and wake up application to perform some action like use CallKit to show incoming call dialog.
5 |
6 | PushNotifications didn't work inside emualtor.
7 |
8 | ## How it works
9 | 1. Your application registers for receiving VoIP notifications
10 | 2. After a successful registration your application adds *device token* to Contact header when REGISTER.
11 | 3. Your SIP server should parse those headers and when someone calling to those user, server should also send push notification for those device(s).
12 | 4. When iOS application receives this PushNotificaiton it should show incoming call dialog via callkit, and register on server.
13 | 5. Server should send INVITE to new registration from this iOS device.
14 | 6. When user press Answer button via callkit, iOS application answers those SIP call.
15 |
16 | ## Client side
17 | When application starts, it should send REGISTER with additional attributes of `Contact` header.
18 |
19 | Your configuration might looks like this
20 | ```javascript
21 | endpoint.createAccount({
22 | "username":"****",
23 | "domain":"****",
24 | "password":"****",
25 | "regContactParams": ";app-id=****;pn-voip-tok=XXXXXXXXX;pn-im-tok=XXXXXXXXXX"
26 | })
27 | ```
28 |
29 | By using react-native-voip-nitifications
30 | 1. Register for *VoIP* notifications.
31 | 2. Obtain `Device token`
32 | 2. Send device token in `Contact` header options by using `contactUriParams` property of account configuration.
33 |
34 | Ensure that the Push Notification Capability is ON
35 |
36 | ## Server side
37 | Your SIP server should support ability to send PushNotifications and also have addtional logic that re-send's INVITE messages during calling to user when new registration is available.
38 | For example an working module for freeswitch consider using *mod_apn*.
39 |
40 | ## Background mode
41 |
42 | When your application goes to background mode it should send UNREGISTER to ensure that PJSIP will send NEW REGISTRATION when VoIP notification will be received from server.
43 | When new registration
44 |
45 | # CallKit
46 |
47 |
48 |
49 |
50 | CallKit app receives an incoming call while it is in the background, the system's native incoming call UI will be shown.
51 |
52 |
53 |
54 |
55 | TODO: Example
--------------------------------------------------------------------------------
/wiki/settings.md:
--------------------------------------------------------------------------------
1 | # Settings
2 |
3 |
4 | # Connectivity settings
5 | TODO
6 |
7 | # Network settings
8 | TODO
9 |
10 |
11 | # Codecs settings
12 | Print codec settings
13 | ```javascript
14 | let endpoint = new Endpoint();
15 | let state = await endpoint.start();
16 | let {accounts, calls, settings, connectivity} = state;
17 |
18 | console.log("codecs", settings.codecs); // Shows a list of available codecs with priority
19 | ```
20 |
21 | Change codec configuration
22 | ```javascript
23 | // Not listed codecs are automatically will have zero priority
24 | endpoint.changeCodecSettings({
25 | "PCMA/8000/1": 0,
26 | "G722/16000/1": 0, // Zero means to disable the codec.
27 | "iLBC/8000/1": 210,
28 | "speex/8000/1": 0,
29 | "speex/16000/1": 0,
30 | "speex/32000/1": 0
31 | })
32 | ```
--------------------------------------------------------------------------------
/wiki/startup.md:
--------------------------------------------------------------------------------
1 |
2 | # Initialization
3 |
4 | First of all you have to initialize module to be able to work with it.
5 |
6 | There are some interesting moment in initialization.
7 | When application goes to background, PJSIP module is still working and able to receive calls, but your javascipt is totally suspended.
8 | When User open your application, javascript start to work and now your js application need to know what status have your account or may be you have pending incoming call.
9 |
10 | So thats why first step should call start method for pjsip module.
11 |
12 | ```
13 | let endpoint = new Endpoint();
14 | let state = await endpoint.start();
15 | let {accounts, calls} = state;
16 | ```
17 |
18 | It works in background because in Android where is a service PjSip service, that you included in AndroidManifest.xml.
19 | TODO: Describe how it works on iOS.
20 |
--------------------------------------------------------------------------------