@nativetalkcommunications/react-native-call-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NativetalkCallSdk.podspec +31 -0
- package/README.md +494 -0
- package/android/build.gradle +58 -0
- package/android/gradle.properties +2 -0
- package/android/src/main/AndroidManifest.xml +84 -0
- package/android/src/main/java/io/nativetalk/callsdk/BackgroundService.kt +149 -0
- package/android/src/main/java/io/nativetalk/callsdk/CallActionReceiver.kt +24 -0
- package/android/src/main/java/io/nativetalk/callsdk/CallService.kt +45 -0
- package/android/src/main/java/io/nativetalk/callsdk/Compatibility.kt +96 -0
- package/android/src/main/java/io/nativetalk/callsdk/CoreManager.kt +801 -0
- package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallScreeningService.kt +105 -0
- package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallSdkModule.kt +205 -0
- package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallSdkPackage.kt +18 -0
- package/android/src/main/java/io/nativetalk/callsdk/TelephonyMonitor.kt +229 -0
- package/android/src/main/java/io/nativetalk/callsdk/Utils.kt +42 -0
- package/android/src/main/res/drawable/ic_nativetalk_call.xml +9 -0
- package/android/src/main/res/values/strings.xml +9 -0
- package/app.plugin.js +1 -0
- package/ios/NativetalkCallSdk-Bridging-Header.h +4 -0
- package/ios/NativetalkCallSdk.swift +738 -0
- package/ios/NativetalkCallSdkBridge.m +35 -0
- package/lib/commonjs/CallProvider.js +602 -0
- package/lib/commonjs/helpers.js +173 -0
- package/lib/commonjs/index.js +96 -0
- package/lib/commonjs/native.js +146 -0
- package/lib/commonjs/types.js +8 -0
- package/lib/commonjs/ui/Avatar.js +29 -0
- package/lib/commonjs/ui/Dialer.js +189 -0
- package/lib/commonjs/ui/IncomingCallView.js +128 -0
- package/lib/commonjs/ui/OutgoingCallView.js +117 -0
- package/lib/commonjs/ui/index.js +22 -0
- package/lib/commonjs/ui/theme.js +21 -0
- package/lib/module/CallProvider.js +573 -0
- package/lib/module/helpers.js +161 -0
- package/lib/module/index.js +57 -0
- package/lib/module/native.js +123 -0
- package/lib/module/types.js +7 -0
- package/lib/module/ui/Avatar.js +22 -0
- package/lib/module/ui/Dialer.js +162 -0
- package/lib/module/ui/IncomingCallView.js +101 -0
- package/lib/module/ui/OutgoingCallView.js +110 -0
- package/lib/module/ui/index.js +13 -0
- package/lib/module/ui/theme.js +17 -0
- package/lib/typescript/CallProvider.d.ts +46 -0
- package/lib/typescript/helpers.d.ts +52 -0
- package/lib/typescript/index.d.ts +77 -0
- package/lib/typescript/native.d.ts +53 -0
- package/lib/typescript/types.d.ts +155 -0
- package/lib/typescript/ui/Avatar.d.ts +13 -0
- package/lib/typescript/ui/Dialer.d.ts +29 -0
- package/lib/typescript/ui/IncomingCallView.d.ts +39 -0
- package/lib/typescript/ui/OutgoingCallView.d.ts +28 -0
- package/lib/typescript/ui/index.d.ts +13 -0
- package/lib/typescript/ui/theme.d.ts +20 -0
- package/linphonesw-pod/Sources/LinphoneSdkInfos.swift +4 -0
- package/linphonesw-pod/Sources/LinphoneWrapper.swift +42949 -0
- package/linphonesw-pod/linphonesw.podspec +46 -0
- package/package.json +90 -0
- package/plugin/build/index.js +12 -0
- package/plugin/build/withAndroid.js +78 -0
- package/plugin/build/withIos.js +66 -0
- package/src/CallProvider.tsx +675 -0
- package/src/helpers.ts +179 -0
- package/src/index.ts +84 -0
- package/src/native.ts +185 -0
- package/src/types.ts +202 -0
- package/src/ui/Avatar.tsx +46 -0
- package/src/ui/Dialer.tsx +248 -0
- package/src/ui/IncomingCallView.tsx +161 -0
- package/src/ui/OutgoingCallView.tsx +203 -0
- package/src/ui/index.ts +13 -0
- package/src/ui/theme.ts +36 -0
- package/ui/package.json +6 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tech4mation Limited / Nativetalk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,31 @@
|
|
|
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 = "NativetalkCallSdk"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["repository"]["url"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = { "Tech4mation" => "engineering@tech4mation.com" }
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => "13.0" }
|
|
14
|
+
s.source = { :git => package["repository"]["url"], :tag => "v#{s.version}" }
|
|
15
|
+
|
|
16
|
+
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
17
|
+
s.requires_arc = true
|
|
18
|
+
s.swift_version = "5.0"
|
|
19
|
+
|
|
20
|
+
s.dependency "React-Core"
|
|
21
|
+
|
|
22
|
+
# linphonesw is bundled inside the npm package at linphonesw-pod/.
|
|
23
|
+
# The Podfile must reference it via:
|
|
24
|
+
# pod 'linphonesw', :path => '../node_modules/@nativetalkcommunications/react-native-call-sdk/linphonesw-pod'
|
|
25
|
+
# The config plugin adds this line automatically for Expo users.
|
|
26
|
+
s.dependency "linphonesw"
|
|
27
|
+
|
|
28
|
+
s.pod_target_xcconfig = {
|
|
29
|
+
'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'NO',
|
|
30
|
+
}
|
|
31
|
+
end
|
package/README.md
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
# @nativetalkcommunications/react-native-call-sdk
|
|
2
|
+
|
|
3
|
+
> Plug-and-play SIP / VoIP calling for React Native, powered by [Linphone](https://www.linphone.org/).
|
|
4
|
+
>
|
|
5
|
+
> Drop-in `<CallProvider>`, a `useCall()` hook, optional UI screens — and a
|
|
6
|
+
> native Android + iOS layer that handles backgrounding, foreground services,
|
|
7
|
+
> notifications, and CallKit/Push integration so you don't have to.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Highlights
|
|
12
|
+
|
|
13
|
+
| Feature | Notes |
|
|
14
|
+
|---|---|
|
|
15
|
+
| **Plug-and-play** | One provider, one hook. No coupling to your auth, navigation, or HTTP client. |
|
|
16
|
+
| **Cross-platform** | Android (Linphone SDK 5.x) and iOS (Linphone 5.x + CallKit-ready). |
|
|
17
|
+
| **Backgrounded calls** | Android foreground service keeps the registration warm. iOS supports VoIP push. |
|
|
18
|
+
| **Bundled UI** | Optional `<Dialer />`, `<IncomingCallView />`, `<OutgoingCallView />`. Use them or roll your own. |
|
|
19
|
+
| **Typed** | First-class TypeScript types throughout. |
|
|
20
|
+
| **No coupling** | No `useAuth`, no navigation lib, no axios. You inject everything. |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Requirements
|
|
25
|
+
|
|
26
|
+
- React Native ≥ 0.73
|
|
27
|
+
- iOS ≥ 13.0
|
|
28
|
+
- Android `minSdkVersion` ≥ 24 (Android 7.0)
|
|
29
|
+
- Linphone SDK 5.4.x (Android pulled automatically via Maven; iOS xcframeworks downloaded automatically on first `pod install`)
|
|
30
|
+
- **React Native < 0.82:** set `newArchEnabled=false` in `android/gradle.properties`. React Native ≥ 0.82 runs New Architecture by default and the SDK works via the interop layer — the flag is not needed.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install @nativetalkcommunications/react-native-call-sdk
|
|
38
|
+
# or
|
|
39
|
+
yarn add @nativetalkcommunications/react-native-call-sdk
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Expo setup
|
|
45
|
+
|
|
46
|
+
If your app uses Expo, the config plugin handles all native configuration automatically.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
### Installing from npm
|
|
51
|
+
|
|
52
|
+
#### 1. Add the plugin to `app.json`
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"expo": {
|
|
57
|
+
"plugins": [
|
|
58
|
+
"@nativetalkcommunications/react-native-call-sdk"
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### 2. Run prebuild
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx expo prebuild
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This automatically configures:
|
|
71
|
+
- **Android** — adds the Linphone Maven repository to `android/build.gradle`
|
|
72
|
+
- **iOS** — adds `NSMicrophoneUsageDescription` and `UIBackgroundModes` to `Info.plist`, and adds the `pod 'linphonesw'` line to the `Podfile`
|
|
73
|
+
|
|
74
|
+
Prebuild also runs `pod install` automatically. On the first run, the linphonesw pod downloads the Linphone xcframeworks (~90 seconds, one-time per machine). No SPM step, no manual setup.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
### Installing from a local path (development only)
|
|
79
|
+
|
|
80
|
+
Use this when installing via `npm install file:../nativetalk-call-sdk` during SDK development.
|
|
81
|
+
|
|
82
|
+
#### 1. Add the plugin to `app.json`
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"expo": {
|
|
87
|
+
"plugins": [
|
|
88
|
+
"@nativetalkcommunications/react-native-call-sdk"
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### 2. Update `metro.config.js`
|
|
95
|
+
|
|
96
|
+
If your project does not have a `metro.config.js`, create one at the project root. If it already exists, merge the SDK entries into your existing config:
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
const { getDefaultConfig } = require('expo/metro-config');
|
|
100
|
+
const { mergeConfig } = require('@react-native/metro-config');
|
|
101
|
+
const path = require('path');
|
|
102
|
+
|
|
103
|
+
const sdkPath = path.resolve(__dirname, '../nativetalk-call-sdk'); // adjust path as needed
|
|
104
|
+
|
|
105
|
+
const sdkConfig = {
|
|
106
|
+
watchFolders: [sdkPath],
|
|
107
|
+
resolver: {
|
|
108
|
+
unstable_enableSymlinks: true,
|
|
109
|
+
extraNodeModules: {
|
|
110
|
+
'@nativetalkcommunications/react-native-call-sdk': sdkPath,
|
|
111
|
+
'react': path.resolve(__dirname, 'node_modules/react'),
|
|
112
|
+
'react-native': path.resolve(__dirname, 'node_modules/react-native'),
|
|
113
|
+
},
|
|
114
|
+
nodeModulesPaths: [
|
|
115
|
+
path.resolve(__dirname, 'node_modules'),
|
|
116
|
+
path.resolve(sdkPath, 'node_modules'),
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
module.exports = mergeConfig(getDefaultConfig(__dirname), sdkConfig);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### 3. Remove duplicate react/react-native from the SDK
|
|
125
|
+
|
|
126
|
+
Check if duplicates exist and delete them:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
ls ../nativetalk-call-sdk/node_modules | grep react # check first
|
|
130
|
+
rm -rf ../nativetalk-call-sdk/node_modules/react
|
|
131
|
+
rm -rf ../nativetalk-call-sdk/node_modules/react-native
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### 4. Run prebuild
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npx expo prebuild
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Same as the npm install path — the plugin configures Android and iOS automatically.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### Plugin options
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"expo": {
|
|
149
|
+
"plugins": [
|
|
150
|
+
["@nativetalkcommunications/react-native-call-sdk", {
|
|
151
|
+
"microphonePermission": "This app needs microphone access for voice calls."
|
|
152
|
+
}]
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
| Option | Default | Description |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| `microphonePermission` | `"Microphone access is required for calls."` | iOS microphone permission dialog text |
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## React Native CLI setup
|
|
165
|
+
|
|
166
|
+
If you are **not** using Expo, follow the Android and iOS setup sections below manually.
|
|
167
|
+
|
|
168
|
+
### Local installs only — update Metro config
|
|
169
|
+
|
|
170
|
+
**Skip this step for published npm installs.** If installing from a local path (e.g. `"file:../nativetalk-call-sdk"`), merge the following into your `metro.config.js`. If you already have a `watchFolders`, `extraNodeModules`, or `nodeModulesPaths` config, add the SDK entries to your existing arrays/objects rather than replacing them.
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
|
|
174
|
+
const path = require('path');
|
|
175
|
+
|
|
176
|
+
const sdkPath = path.resolve(__dirname, '../nativetalk-call-sdk'); // adjust path as needed
|
|
177
|
+
|
|
178
|
+
const sdkConfig = {
|
|
179
|
+
watchFolders: [sdkPath],
|
|
180
|
+
resolver: {
|
|
181
|
+
unstable_enableSymlinks: true,
|
|
182
|
+
extraNodeModules: {
|
|
183
|
+
'@nativetalkcommunications/react-native-call-sdk': sdkPath,
|
|
184
|
+
'react': path.resolve(__dirname, 'node_modules/react'),
|
|
185
|
+
'react-native': path.resolve(__dirname, 'node_modules/react-native'),
|
|
186
|
+
},
|
|
187
|
+
nodeModulesPaths: [
|
|
188
|
+
path.resolve(__dirname, 'node_modules'),
|
|
189
|
+
path.resolve(sdkPath, 'node_modules'),
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
module.exports = mergeConfig(getDefaultConfig(__dirname), sdkConfig);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
> **If your metro.config.js already calls `mergeConfig`**, pass `sdkConfig` as an additional argument: `mergeConfig(getDefaultConfig(__dirname), yourExistingConfig, sdkConfig)`.
|
|
198
|
+
|
|
199
|
+
### Local installs only — remove duplicate react/react-native
|
|
200
|
+
|
|
201
|
+
**Skip this step for published npm installs.** Check if duplicates exist and delete them:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
ls ../nativetalk-call-sdk/node_modules | grep react # check first
|
|
205
|
+
rm -rf ../nativetalk-call-sdk/node_modules/react
|
|
206
|
+
rm -rf ../nativetalk-call-sdk/node_modules/react-native
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Android setup
|
|
212
|
+
|
|
213
|
+
### 1. Add the Linphone Maven repository
|
|
214
|
+
|
|
215
|
+
The Linphone SDK is not published to Maven Central. You need to add their repository to `android/settings.gradle`.
|
|
216
|
+
|
|
217
|
+
**If your `settings.gradle` already has a `dependencyResolutionManagement` block**, add the Linphone `maven { }` entry inside the existing `repositories { }` block.
|
|
218
|
+
|
|
219
|
+
**If your `settings.gradle` does not have a `dependencyResolutionManagement` block** (common in fresh RN 0.73+ projects), add the entire block at the bottom of the file:
|
|
220
|
+
|
|
221
|
+
```groovy
|
|
222
|
+
// android/settings.gradle — add at the bottom
|
|
223
|
+
dependencyResolutionManagement {
|
|
224
|
+
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
|
|
225
|
+
repositories {
|
|
226
|
+
google()
|
|
227
|
+
mavenCentral()
|
|
228
|
+
maven { url "https://www.jitpack.io" }
|
|
229
|
+
maven {
|
|
230
|
+
name = "linphone.org maven repository"
|
|
231
|
+
url = uri("https://download.linphone.org/maven_repository")
|
|
232
|
+
content {
|
|
233
|
+
includeGroup("org.linphone")
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
> **Important:** `repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)` means only repositories listed in this block are used — adding repos elsewhere (e.g. `android/build.gradle`) will be silently ignored. Make sure `google()` and `mavenCentral()` are included here too.
|
|
241
|
+
|
|
242
|
+
Without the Linphone repo your build will fail with:
|
|
243
|
+
```
|
|
244
|
+
Could not find org.linphone:linphone-sdk-android:5.4.x.
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
> **Wrong URL**: The older Linphone URL (`linphone.org/maven/repository`) is dead. Use `download.linphone.org/maven_repository` as shown above.
|
|
248
|
+
|
|
249
|
+
### 2. Disable New Architecture
|
|
250
|
+
|
|
251
|
+
The SDK uses the Old Architecture (NativeModules/NativeEventEmitter) bridge. Open `android/gradle.properties` and set:
|
|
252
|
+
|
|
253
|
+
```properties
|
|
254
|
+
newArchEnabled=false
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
If you previously had this set to `true`, you must do a clean reinstall:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# Uninstall the old APK from the device first
|
|
261
|
+
adb uninstall com.yourapp
|
|
262
|
+
|
|
263
|
+
# Then full clean rebuild
|
|
264
|
+
cd android && ./gradlew clean && cd ..
|
|
265
|
+
npx react-native run-android
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Without the clean uninstall, the old New Architecture binary might remain on-device and could crash with:
|
|
269
|
+
```
|
|
270
|
+
PlatformConstants could not be found. Verify that it is available as a TurboModule.
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### 3. Autolink
|
|
274
|
+
|
|
275
|
+
No `MainApplication` edits needed — the SDK autolinks.
|
|
276
|
+
|
|
277
|
+
### 4. Runtime permissions
|
|
278
|
+
|
|
279
|
+
`RECORD_AUDIO` must be granted before the first call. The provider handles this automatically when `requestMicPermission={true}` (the default). To request it manually:
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
import { CallEngine } from '@nativetalkcommunications/react-native-call-sdk';
|
|
283
|
+
await CallEngine.requestMicPermission(); // Android only, no-op on iOS
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### 5. Inbound calls — use a real device for testing
|
|
287
|
+
|
|
288
|
+
Inbound calls do not work on the Android emulator. The emulator is behind a nested NAT (`10.0.2.15` is not reachable from outside). The SIP server cannot route an INVITE back to the emulator's contact address, so the caller gets "number does not exist" and Linphone never sees the INVITE.
|
|
289
|
+
|
|
290
|
+
Outbound calls, registration, and all other features work fine on the emulator.
|
|
291
|
+
|
|
292
|
+
**Test inbound calls on a real Android device.**
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## iOS setup
|
|
297
|
+
|
|
298
|
+
### 1. Declare linphonesw in your Podfile
|
|
299
|
+
|
|
300
|
+
The SDK bundles a self-contained `linphonesw-pod` inside the npm package. Add one line to your `ios/Podfile` inside the target block:
|
|
301
|
+
|
|
302
|
+
```ruby
|
|
303
|
+
target 'YourApp' do
|
|
304
|
+
config = use_native_modules!
|
|
305
|
+
|
|
306
|
+
pod 'linphonesw', :path => '../node_modules/@nativetalkcommunications/react-native-call-sdk/linphonesw-pod'
|
|
307
|
+
|
|
308
|
+
# ... rest of Podfile
|
|
309
|
+
end
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Then run:
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
cd ios && pod install
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
On the first install, the pod automatically downloads the Linphone xcframeworks (~90 seconds). This is a one-time operation — CocoaPods caches the result so subsequent installs are instant.
|
|
319
|
+
|
|
320
|
+
> **No SPM step required.** The old workflow of adding the Linphone Swift Package in Xcode is no longer needed.
|
|
321
|
+
|
|
322
|
+
### 3. Info.plist
|
|
323
|
+
|
|
324
|
+
Add to `ios/YourApp/Info.plist`:
|
|
325
|
+
|
|
326
|
+
```xml
|
|
327
|
+
<key>NSMicrophoneUsageDescription</key>
|
|
328
|
+
<string>Microphone access is required for calls.</string>
|
|
329
|
+
|
|
330
|
+
<key>UIBackgroundModes</key>
|
|
331
|
+
<array>
|
|
332
|
+
<string>audio</string>
|
|
333
|
+
<string>voip</string>
|
|
334
|
+
</array>
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Without `NSMicrophoneUsageDescription` your app will crash when Linphone requests the mic. Without `UIBackgroundModes`, iOS will kill the SIP connection when your app is backgrounded.
|
|
338
|
+
|
|
339
|
+
### 4. Xcode capabilities
|
|
340
|
+
|
|
341
|
+
In Xcode → your target → **Signing & Capabilities**:
|
|
342
|
+
|
|
343
|
+
- Add **Background Modes** → tick **Audio, AirPlay and Picture in Picture** and **Voice over IP**
|
|
344
|
+
|
|
345
|
+
This mirrors the `UIBackgroundModes` in Info.plist — both are required.
|
|
346
|
+
|
|
347
|
+
### 5. Simulator vs real device
|
|
348
|
+
|
|
349
|
+
Registration and foreground calls work on the iOS Simulator. What does **not** work on the simulator is PushKit — VoIP push tokens are never delivered to simulators, so background/killed-app inbound calls won't work there. For testing push-driven inbound calls, use a real iPhone.
|
|
350
|
+
|
|
351
|
+
### 6. CallKit + VoIP push (for background/killed-app incoming calls)
|
|
352
|
+
|
|
353
|
+
Without VoIP push, inbound calls only arrive when the SIP socket is already open (app foregrounded). For reliable incoming calls when the app is backgrounded or killed, you need CallKit + PushKit. See [docs/push-notifications.md](docs/push-notifications.md) for the copy-pasteable AppDelegate template.
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Quick start
|
|
358
|
+
|
|
359
|
+
```tsx
|
|
360
|
+
import React, { useState } from 'react';
|
|
361
|
+
import { Alert } from 'react-native';
|
|
362
|
+
import { CallProvider, useCall } from '@nativetalkcommunications/react-native-call-sdk';
|
|
363
|
+
import { Dialer } from '@nativetalkcommunications/react-native-call-sdk/ui';
|
|
364
|
+
|
|
365
|
+
const sip = {
|
|
366
|
+
username: '100',
|
|
367
|
+
password: 'secret',
|
|
368
|
+
domain: 'yourcompany.nativetalk.io', // must be a *.nativetalk.io domain
|
|
369
|
+
transport: 'tcp',
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
export default function App() {
|
|
373
|
+
return (
|
|
374
|
+
<CallProvider
|
|
375
|
+
config={sip}
|
|
376
|
+
onIncomingCall={(info) => console.log('Incoming from', info.phone)}
|
|
377
|
+
onRegistrationStateChanged={(r) => console.log('SIP:', r.state)}
|
|
378
|
+
onError={(e) => Alert.alert('SDK Error', e.message)}
|
|
379
|
+
>
|
|
380
|
+
<Dialer />
|
|
381
|
+
</CallProvider>
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
**Error handling** — all SDK errors are surfaced through `onError`. The callback receives `{ code: string, message: string }`. Common codes:
|
|
387
|
+
|
|
388
|
+
| Code | When |
|
|
389
|
+
|---|---|
|
|
390
|
+
| `INVALID_DOMAIN` | The SIP domain is not a `*.nativetalk.io` domain |
|
|
391
|
+
| `REGISTRATION_FAILED` | SIP server rejected the REGISTER request |
|
|
392
|
+
| `NO_CONFIG` | `register()` was called with no config available |
|
|
393
|
+
| `DIAL_FAILED` | `dial()` was called with no domain configured |
|
|
394
|
+
|
|
395
|
+
The hook gives you everything else:
|
|
396
|
+
|
|
397
|
+
```tsx
|
|
398
|
+
function CallControls() {
|
|
399
|
+
const {
|
|
400
|
+
callStatus,
|
|
401
|
+
formattedDuration,
|
|
402
|
+
isMuted, isSpeaker, isHeld,
|
|
403
|
+
dial, answer, hangup, decline,
|
|
404
|
+
toggleMute, toggleSpeaker, toggleHold,
|
|
405
|
+
sendDtmf,
|
|
406
|
+
} = useCall();
|
|
407
|
+
|
|
408
|
+
return (
|
|
409
|
+
<View>
|
|
410
|
+
<Text>{callStatus} — {formattedDuration}</Text>
|
|
411
|
+
<Button onPress={() => dial('+2348012345678')} title="Dial" />
|
|
412
|
+
<Button onPress={hangup} title="Hang up" />
|
|
413
|
+
</View>
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
## Common errors
|
|
421
|
+
|
|
422
|
+
| Error | Cause | Fix |
|
|
423
|
+
|---|---|---|
|
|
424
|
+
| `Could not find org.linphone:linphone-sdk-android` | Wrong or missing Maven repo | Add `download.linphone.org/maven_repository` to `android/build.gradle` in the `allprojects { repositories { } }` block |
|
|
425
|
+
| `PlatformConstants could not be found` | New Architecture enabled but SDK uses Old Arch | Set `newArchEnabled=false` in `gradle.properties`, uninstall APK, clean rebuild |
|
|
426
|
+
| `TurboModuleRegistry … was not found` | Duplicate react-native in SDK node_modules | Delete `nativetalk-call-sdk/node_modules/react` and `.../react-native` |
|
|
427
|
+
| `Unable to load script` | Metro not running after clean build | Start Metro separately (`npx react-native start`), then run the app |
|
|
428
|
+
| `No podspec found for linphonesw` | Podfile missing the `pod 'linphonesw'` line | Add `pod 'linphonesw', :path => '../node_modules/@nativetalkcommunications/react-native-call-sdk/linphonesw-pod'` to your Podfile |
|
|
429
|
+
| `'jni.h' file not found` | Linphone xcframeworks embedded directly in NativetalkCallSdk module | Ensure linphonesw is a separate pod — do not vendor the xcframeworks directly in NativetalkCallSdk |
|
|
430
|
+
| Inbound calls not received (Android emulator) | Emulator NAT — SIP server can't reach `10.0.2.15` | Test on a real Android device |
|
|
431
|
+
| Inbound calls not received (iOS) | App backgrounded without VoIP push | Wire up CallKit + PushKit; see `docs/push-notifications.md` |
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Documentation
|
|
436
|
+
|
|
437
|
+
| Topic | Where |
|
|
438
|
+
|---|---|
|
|
439
|
+
| **Android setup deep-dive** — services, channels, manifest, Maven repo | [docs/android-setup.md](docs/android-setup.md) |
|
|
440
|
+
| **iOS setup deep-dive** — Linphone SPM, CallKit, PushKit | [docs/ios-setup.md](docs/ios-setup.md) |
|
|
441
|
+
| **Configuration** — every prop on `<CallProvider>` | [docs/configuration.md](docs/configuration.md) |
|
|
442
|
+
| **API reference** — every export, every type | [docs/api-reference.md](docs/api-reference.md) |
|
|
443
|
+
| **Bundled UI components** — props, theming, customization | [docs/ui-components.md](docs/ui-components.md) |
|
|
444
|
+
| **Push notifications** — VoIP push wakeup, FCM data messages | [docs/push-notifications.md](docs/push-notifications.md) |
|
|
445
|
+
| **Architecture** — what's in the box and why | [docs/architecture.md](docs/architecture.md) |
|
|
446
|
+
| **Troubleshooting** — common errors and fixes | [docs/troubleshooting.md](docs/troubleshooting.md) |
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## API surface
|
|
451
|
+
|
|
452
|
+
```ts
|
|
453
|
+
// Main provider + hook
|
|
454
|
+
import { CallProvider, useCall } from '@nativetalkcommunications/react-native-call-sdk';
|
|
455
|
+
|
|
456
|
+
// Types
|
|
457
|
+
import type {
|
|
458
|
+
SipConfig, SipTransport,
|
|
459
|
+
CallApi, CallState, CallLogEntry,
|
|
460
|
+
IncomingCallInfo, RegistrationEvent, RegistrationState,
|
|
461
|
+
DeclineReason,
|
|
462
|
+
CallProviderProps, CallProviderEvents,
|
|
463
|
+
} from '@nativetalkcommunications/react-native-call-sdk';
|
|
464
|
+
|
|
465
|
+
// Helpers
|
|
466
|
+
import {
|
|
467
|
+
formatDuration, // 65 → "1:05"
|
|
468
|
+
callStatusLabel, // "StreamsRunning" → "In progress"
|
|
469
|
+
parseSipUser, // "sip:100@x" → "100"
|
|
470
|
+
sanitizeDial, // strips non-dial chars
|
|
471
|
+
formatTenantDomain, // strips http(s):// and trailing /
|
|
472
|
+
destinationToSipUri, // "100" + "sip.example.com" → "sip:100@sip.example.com"
|
|
473
|
+
} from '@nativetalkcommunications/react-native-call-sdk';
|
|
474
|
+
|
|
475
|
+
// Escape hatch: drive the native module directly (e.g. from headless tasks)
|
|
476
|
+
import { CallEngine } from '@nativetalkcommunications/react-native-call-sdk';
|
|
477
|
+
|
|
478
|
+
// Optional UI
|
|
479
|
+
import {
|
|
480
|
+
Dialer,
|
|
481
|
+
IncomingCallView,
|
|
482
|
+
OutgoingCallView,
|
|
483
|
+
Avatar,
|
|
484
|
+
defaultTheme,
|
|
485
|
+
mergeTheme,
|
|
486
|
+
type CallTheme,
|
|
487
|
+
} from '@nativetalkcommunications/react-native-call-sdk/ui';
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## License
|
|
493
|
+
|
|
494
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Standard React Native library build script.
|
|
2
|
+
// This file is consumed by autolinking in the host app — devs don't run it
|
|
3
|
+
// directly. Keep it minimal and version-agnostic.
|
|
4
|
+
|
|
5
|
+
buildscript {
|
|
6
|
+
repositories {
|
|
7
|
+
google()
|
|
8
|
+
mavenCentral()
|
|
9
|
+
}
|
|
10
|
+
dependencies {
|
|
11
|
+
classpath "com.android.tools.build:gradle:8.1.1"
|
|
12
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
apply plugin: "com.android.library"
|
|
17
|
+
apply plugin: "kotlin-android"
|
|
18
|
+
|
|
19
|
+
def safeExtGet(prop, fallback) {
|
|
20
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
android {
|
|
24
|
+
namespace "io.nativetalk.callsdk"
|
|
25
|
+
compileSdkVersion safeExtGet("compileSdkVersion", 34)
|
|
26
|
+
|
|
27
|
+
defaultConfig {
|
|
28
|
+
minSdkVersion safeExtGet("minSdkVersion", 24)
|
|
29
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 34)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
compileOptions {
|
|
33
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
34
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
35
|
+
}
|
|
36
|
+
kotlinOptions {
|
|
37
|
+
jvmTarget = "17"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
lintOptions {
|
|
41
|
+
abortOnError false
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
repositories {
|
|
46
|
+
mavenCentral()
|
|
47
|
+
google()
|
|
48
|
+
maven { url "https://download.linphone.org/maven_repository" }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
dependencies {
|
|
52
|
+
implementation "com.facebook.react:react-android"
|
|
53
|
+
implementation "androidx.core:core-ktx:1.12.0"
|
|
54
|
+
implementation "androidx.appcompat:appcompat:1.6.1"
|
|
55
|
+
|
|
56
|
+
// Linphone SDK — the SIP/VoIP engine.
|
|
57
|
+
implementation "org.linphone:linphone-sdk-android:5.4.44"
|
|
58
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<manifest
|
|
2
|
+
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
xmlns:tools="http://schemas.android.com/tools">
|
|
4
|
+
|
|
5
|
+
<!-- Basic functionality -->
|
|
6
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
7
|
+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
8
|
+
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
9
|
+
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
|
10
|
+
<uses-permission android:name="android.permission.VIBRATE" />
|
|
11
|
+
|
|
12
|
+
<!-- Audio & calling core -->
|
|
13
|
+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
|
14
|
+
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
|
15
|
+
<uses-permission android:name="android.permission.BLUETOOTH" />
|
|
16
|
+
|
|
17
|
+
<!-- Telecom framework (modern VoIP) -->
|
|
18
|
+
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
|
|
19
|
+
<uses-permission android:name="android.permission.CALL_PHONE" />
|
|
20
|
+
|
|
21
|
+
<!-- Foreground service requirements (Android 14+) -->
|
|
22
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
23
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
|
|
24
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
|
|
25
|
+
|
|
26
|
+
<!-- Notifications & UI -->
|
|
27
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
28
|
+
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
|
|
29
|
+
|
|
30
|
+
<!--
|
|
31
|
+
Contacts permission is optional — only required if you opt in to the
|
|
32
|
+
contact-lookup feature on the device call observer. Apps that don't
|
|
33
|
+
need it can suppress at the app level.
|
|
34
|
+
-->
|
|
35
|
+
<uses-permission android:name="android.permission.READ_CONTACTS" tools:node="merge" />
|
|
36
|
+
|
|
37
|
+
<application>
|
|
38
|
+
<!--
|
|
39
|
+
Linphone's internal service that drives core.iterate() — the
|
|
40
|
+
message-processing loop that handles incoming SIP INVITEs.
|
|
41
|
+
Without this declaration Android rejects the start() call and
|
|
42
|
+
inbound calls are never received.
|
|
43
|
+
-->
|
|
44
|
+
<service
|
|
45
|
+
android:name="org.linphone.core.tools.service.CoreService"
|
|
46
|
+
android:enabled="true"
|
|
47
|
+
android:exported="false"
|
|
48
|
+
android:foregroundServiceType="phoneCall|microphone" />
|
|
49
|
+
|
|
50
|
+
<service
|
|
51
|
+
android:name="io.nativetalk.callsdk.CallService"
|
|
52
|
+
android:enabled="true"
|
|
53
|
+
android:exported="false"
|
|
54
|
+
android:foregroundServiceType="phoneCall|microphone">
|
|
55
|
+
<intent-filter>
|
|
56
|
+
<action android:name="io.nativetalk.callsdk.CallService" />
|
|
57
|
+
</intent-filter>
|
|
58
|
+
</service>
|
|
59
|
+
|
|
60
|
+
<service
|
|
61
|
+
android:name="io.nativetalk.callsdk.BackgroundService"
|
|
62
|
+
android:enabled="true"
|
|
63
|
+
android:exported="false"
|
|
64
|
+
android:foregroundServiceType="phoneCall" />
|
|
65
|
+
|
|
66
|
+
<service
|
|
67
|
+
android:name="io.nativetalk.callsdk.NativetalkCallScreeningService"
|
|
68
|
+
android:permission="android.permission.BIND_SCREENING_SERVICE"
|
|
69
|
+
android:exported="true">
|
|
70
|
+
<intent-filter>
|
|
71
|
+
<action android:name="android.telecom.CallScreeningService" />
|
|
72
|
+
</intent-filter>
|
|
73
|
+
</service>
|
|
74
|
+
|
|
75
|
+
<receiver
|
|
76
|
+
android:name="io.nativetalk.callsdk.CallActionReceiver"
|
|
77
|
+
android:exported="false">
|
|
78
|
+
<intent-filter>
|
|
79
|
+
<action android:name="io.nativetalk.callsdk.ACTION_ANSWER" />
|
|
80
|
+
<action android:name="io.nativetalk.callsdk.ACTION_DECLINE" />
|
|
81
|
+
</intent-filter>
|
|
82
|
+
</receiver>
|
|
83
|
+
</application>
|
|
84
|
+
</manifest>
|