├── .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 | ![Android Pending Intent PjSip](android_notification_example.png) 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 | --------------------------------------------------------------------------------