@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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/NativetalkCallSdk.podspec +31 -0
  3. package/README.md +494 -0
  4. package/android/build.gradle +58 -0
  5. package/android/gradle.properties +2 -0
  6. package/android/src/main/AndroidManifest.xml +84 -0
  7. package/android/src/main/java/io/nativetalk/callsdk/BackgroundService.kt +149 -0
  8. package/android/src/main/java/io/nativetalk/callsdk/CallActionReceiver.kt +24 -0
  9. package/android/src/main/java/io/nativetalk/callsdk/CallService.kt +45 -0
  10. package/android/src/main/java/io/nativetalk/callsdk/Compatibility.kt +96 -0
  11. package/android/src/main/java/io/nativetalk/callsdk/CoreManager.kt +801 -0
  12. package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallScreeningService.kt +105 -0
  13. package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallSdkModule.kt +205 -0
  14. package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallSdkPackage.kt +18 -0
  15. package/android/src/main/java/io/nativetalk/callsdk/TelephonyMonitor.kt +229 -0
  16. package/android/src/main/java/io/nativetalk/callsdk/Utils.kt +42 -0
  17. package/android/src/main/res/drawable/ic_nativetalk_call.xml +9 -0
  18. package/android/src/main/res/values/strings.xml +9 -0
  19. package/app.plugin.js +1 -0
  20. package/ios/NativetalkCallSdk-Bridging-Header.h +4 -0
  21. package/ios/NativetalkCallSdk.swift +738 -0
  22. package/ios/NativetalkCallSdkBridge.m +35 -0
  23. package/lib/commonjs/CallProvider.js +602 -0
  24. package/lib/commonjs/helpers.js +173 -0
  25. package/lib/commonjs/index.js +96 -0
  26. package/lib/commonjs/native.js +146 -0
  27. package/lib/commonjs/types.js +8 -0
  28. package/lib/commonjs/ui/Avatar.js +29 -0
  29. package/lib/commonjs/ui/Dialer.js +189 -0
  30. package/lib/commonjs/ui/IncomingCallView.js +128 -0
  31. package/lib/commonjs/ui/OutgoingCallView.js +117 -0
  32. package/lib/commonjs/ui/index.js +22 -0
  33. package/lib/commonjs/ui/theme.js +21 -0
  34. package/lib/module/CallProvider.js +573 -0
  35. package/lib/module/helpers.js +161 -0
  36. package/lib/module/index.js +57 -0
  37. package/lib/module/native.js +123 -0
  38. package/lib/module/types.js +7 -0
  39. package/lib/module/ui/Avatar.js +22 -0
  40. package/lib/module/ui/Dialer.js +162 -0
  41. package/lib/module/ui/IncomingCallView.js +101 -0
  42. package/lib/module/ui/OutgoingCallView.js +110 -0
  43. package/lib/module/ui/index.js +13 -0
  44. package/lib/module/ui/theme.js +17 -0
  45. package/lib/typescript/CallProvider.d.ts +46 -0
  46. package/lib/typescript/helpers.d.ts +52 -0
  47. package/lib/typescript/index.d.ts +77 -0
  48. package/lib/typescript/native.d.ts +53 -0
  49. package/lib/typescript/types.d.ts +155 -0
  50. package/lib/typescript/ui/Avatar.d.ts +13 -0
  51. package/lib/typescript/ui/Dialer.d.ts +29 -0
  52. package/lib/typescript/ui/IncomingCallView.d.ts +39 -0
  53. package/lib/typescript/ui/OutgoingCallView.d.ts +28 -0
  54. package/lib/typescript/ui/index.d.ts +13 -0
  55. package/lib/typescript/ui/theme.d.ts +20 -0
  56. package/linphonesw-pod/Sources/LinphoneSdkInfos.swift +4 -0
  57. package/linphonesw-pod/Sources/LinphoneWrapper.swift +42949 -0
  58. package/linphonesw-pod/linphonesw.podspec +46 -0
  59. package/package.json +90 -0
  60. package/plugin/build/index.js +12 -0
  61. package/plugin/build/withAndroid.js +78 -0
  62. package/plugin/build/withIos.js +66 -0
  63. package/src/CallProvider.tsx +675 -0
  64. package/src/helpers.ts +179 -0
  65. package/src/index.ts +84 -0
  66. package/src/native.ts +185 -0
  67. package/src/types.ts +202 -0
  68. package/src/ui/Avatar.tsx +46 -0
  69. package/src/ui/Dialer.tsx +248 -0
  70. package/src/ui/IncomingCallView.tsx +161 -0
  71. package/src/ui/OutgoingCallView.tsx +203 -0
  72. package/src/ui/index.ts +13 -0
  73. package/src/ui/theme.ts +36 -0
  74. 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,2 @@
1
+ android.useAndroidX=true
2
+ android.enableJetifier=true
@@ -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>