From a2b43cad8c2544151c8c62afa577ebc9f762711b Mon Sep 17 00:00:00 2001 From: Artem Grintsevich Date: Thu, 9 Apr 2026 12:18:29 +0200 Subject: [PATCH] chore: updated package docs --- packages/react-native-callingx/README.md | 404 +++-------------------- 1 file changed, 48 insertions(+), 356 deletions(-) diff --git a/packages/react-native-callingx/README.md b/packages/react-native-callingx/README.md index e549f5d000..091d18c090 100644 --- a/packages/react-native-callingx/README.md +++ b/packages/react-native-callingx/README.md @@ -1,395 +1,87 @@ -# react-native-callingx +# @stream-io/react-native-callingx -A React Native Turbo Module for seamless native calling integration. This library provides a unified API to integrate with **CallKit** on iOS and the **Telecom/ConnectionService** API on Android, enabling your app to display system-level calling UI and interact with native call controls. +React Native native-calling bridge for: -## Features +- iOS CallKit +- Android Telecom/ConnectionService -- 📞 **Incoming call UI** — Display native incoming call screens (even when the app is killed) -- 📲 **Outgoing call registration** — Register outgoing calls with the system -- 🎛️ **Call controls** — Mute, hold, end calls with native system integration -- 🔔 **Custom notifications** — Configurable Android notification channels -- ⚡ **Turbo Module** — Built with the New Architecture for optimal performance -- 📱 **Background support** — Handle calls when the app is backgrounded or killed - -## Requirements - -- React Native 0.73+ (New Architecture / Turbo Modules) -- iOS 13.0+ -- Android API 26+ (Android 8.0 Oreo) - -## Installation +## Install ```sh -npm install @stream-io/react-native-callingx -# or yarn add @stream-io/react-native-callingx ``` -### iOS Setup - -1. Add the required background modes to your `Info.plist`: - -```xml -UIBackgroundModes - - voip - audio - -``` - -2. Run pod install: +Then run iOS pods: ```sh cd ios && pod install ``` -3. For VoIP push notifications, configure your `AppDelegate` to report incoming calls: - -```objc -#import - -- (void)pushRegistry:(PKPushRegistry *)registry -didReceiveIncomingPushWithPayload:(PKPushPayload *)payload - forType:(PKPushType)type -withCompletionHandler:(void (^)(void))completion { - - // Extract call information from payload - NSString *callId = payload.dictionaryPayload[@"call_id"]; - NSString *callerName = payload.dictionaryPayload[@"caller_name"]; - NSString *handle = payload.dictionaryPayload[@"handle"]; - BOOL hasVideo = [payload.dictionaryPayload[@"has_video"] boolValue]; - - [Callingx reportNewIncomingCall:callId - handle:handle - handleType:@"generic" - hasVideo:hasVideo - localizedCallerName:callerName - supportsHolding:YES - supportsDTMF:NO - supportsGrouping:NO - supportsUngrouping:NO - payload:payload.dictionaryPayload - withCompletionHandler:completion]; -} -``` - -### Android Setup +## Quick start -## Usage +```ts +import { CallingxModule } from '@stream-io/react-native-callingx'; -### Setup - -Initialize the module with platform-specific configuration: - -```typescript -import { CallingxModule } from 'react-native-callingx'; - -// Setup must be called before any other method CallingxModule.setup({ ios: { - appName: 'My App', supportsVideo: true, - maximumCallsPerCallGroup: 1, - maximumCallGroups: 1, - handleType: 'generic', // 'generic' | 'number' | 'phone' | 'email' + callsHistory: false, }, android: { incomingChannel: { - id: 'incoming_calls', - name: 'Incoming Calls', - sound: 'ringtone', // optional custom sound - vibration: true, + id: 'incoming_calls_channel', + name: 'Incoming calls', }, - outgoingChannel: { - id: 'ongoing_calls', - name: 'Ongoing Calls', + ongoingChannel: { + id: 'ongoing_calls_channel', + name: 'Ongoing calls', }, - // Optional: transform display text - titleTransformer: (name) => `Call from ${name}`, - subtitleTransformer: (phoneNumber) => phoneNumber, }, }); -``` - -### Request Permissions - -Before displaying calls, request the required permissions: -```typescript -const permissions = await CallingxModule.requestPermissions(); -console.log('Audio permission:', permissions.recordAudio); -console.log('Notification permission:', permissions.postNotifications); -``` - -### Display Incoming Call - -Show the native incoming call UI: - -```typescript await CallingxModule.displayIncomingCall( - 'unique-call-id', - '+1234567890', // phone number / handle - 'John Doe', // caller name - true, // has video -); -``` - -### Start Outgoing Call - -Register an outgoing call with the system: - -```typescript -await CallingxModule.startCall( - 'unique-call-id', - '+1234567890', + 'call-id', + '+123456789', 'John Doe', - false, // audio only -); -``` - -### Answer Call - -Answer an incoming call programmatically: - -```typescript -await CallingxModule.answerIncomingCall('unique-call-id'); -``` - -### Activate Call - -Mark a call as active (connected): - -```typescript -await CallingxModule.setCurrentCallActive('unique-call-id'); -``` - -### End Call - -End a call with a specific reason: - -```typescript -import type { EndCallReason } from 'react-native-callingx'; - -// Available reasons: -// 'local' | 'remote' | 'rejected' | 'busy' | 'answeredElsewhere' | -// 'missed' | 'error' | 'canceled' | 'restricted' | 'unknown' -await CallingxModule.endCallWithReason('unique-call-id', 'remote'); -``` - -### Mute/Unmute - -Toggle call mute state: - -```typescript -await CallingxModule.setMutedCall('unique-call-id', true); // mute -await CallingxModule.setMutedCall('unique-call-id', false); // unmute -``` - -### Hold/Unhold - -Toggle call hold state: - -```typescript -await CallingxModule.setOnHoldCall('unique-call-id', true); // hold -await CallingxModule.setOnHoldCall('unique-call-id', false); // unhold -``` - -### Update Display - -Update the caller information during a call: - -```typescript -await CallingxModule.updateDisplay( - 'unique-call-id', - '+1234567890', - 'Updated Name', + true, ); ``` -### Event Listeners - -Subscribe to call events: - -```typescript -import { CallingxModule } from 'react-native-callingx'; -import type { EventName } from 'react-native-callingx'; - -// Answer event - user answered from system UI -const answerSubscription = CallingxModule.addEventListener( - 'answerCall', - (params) => { - console.log('Call answered:', params.callId); - }, -); - -// End event - call ended -const endSubscription = CallingxModule.addEventListener('endCall', (params) => { - console.log('Call ended:', params.callId, 'Cause:', params.cause); -}); - -// Hold toggle event -const holdSubscription = CallingxModule.addEventListener( - 'didToggleHoldCallAction', - (params) => { - console.log('Hold toggled:', params.callId, 'On hold:', params.hold); - }, -); - -// Mute toggle event -const muteSubscription = CallingxModule.addEventListener( - 'didPerformSetMutedCallAction', - (params) => { - console.log('Mute toggled:', params.callId, 'Muted:', params.muted); - }, -); - -// Start call action (outgoing call initiated from system) -const startSubscription = CallingxModule.addEventListener( - 'didReceiveStartCallAction', - (params) => { - console.log('Start call action:', params.callId); - }, -); - -// Clean up when done -answerSubscription.remove(); -endSubscription.remove(); -// ... remove other subscriptions -``` - -### Handle Initial Events - -When the app is launched from a killed state by a call action, retrieve queued events: - -```typescript -// Get events that occurred before the module was initialized -const initialEvents = CallingxModule.getInitialEvents(); -initialEvents.forEach((event) => { - console.log('Initial event:', event.eventName, event.params); -}); - -// Clear initial events after processing -await CallingxModule.clearInitialEvents(); -``` - -### Background Tasks (Android) - -Run background tasks for call-related operations: - -```typescript -// Start a managed background task -await CallingxModule.startBackgroundTask(async (taskData, stopTask) => { - try { - // Perform background work (e.g., connect to call server) - await connectToCallServer(); - } finally { - stopTask(); // Always call when done - } -}); - -// Or stop manually -await CallingxModule.stopBackgroundTask(); -``` - -## API Reference - -### CallingxModule - -| Method | Description | -| ---------------------------------------------------------------- | ---------------------------------------------------- | -| `setup(options)` | Initialize the module with platform-specific options | -| `requestPermissions()` | Request required permissions (audio, notifications) | -| `checkPermissions()` | Check current permission status | -| `displayIncomingCall(callId, phoneNumber, callerName, hasVideo)` | Display incoming call UI | -| `answerIncomingCall(callId)` | Answer an incoming call | -| `startCall(callId, phoneNumber, callerName, hasVideo)` | Register an outgoing call | -| `setCurrentCallActive(callId)` | Mark call as active/connected | -| `updateDisplay(callId, phoneNumber, callerName)` | Update caller display info | -| `endCallWithReason(callId, reason)` | End call with specified reason | -| `setMutedCall(callId, isMuted)` | Toggle call mute state | -| `setOnHoldCall(callId, isOnHold)` | Toggle call hold state | -| `addEventListener(eventName, callback)` | Subscribe to call events | -| `getInitialEvents()` | Get queued events from app launch | -| `clearInitialEvents()` | Clear queued initial events | -| `startBackgroundTask(taskProvider)` | Start Android background task | -| `stopBackgroundTask()` | Stop Android background task | -| `log(message, level)` | Log message to native console | - -### Events - -| Event | Parameters | Description | -| ------------------------------ | ------------------- | --------------------------------- | -| `answerCall` | `{ callId }` | User answered call from system UI | -| `endCall` | `{ callId, cause }` | Call ended | -| `didToggleHoldCallAction` | `{ callId, hold }` | Hold state changed | -| `didPerformSetMutedCallAction` | `{ callId, muted }` | Mute state changed | -| `didReceiveStartCallAction` | `{ callId }` | Outgoing call action received | - -### Types - -```typescript -type EndCallReason = - | 'local' // Call ended by the local user (e.g., hanging up) - | 'remote' // Call ended by the remote party, or outgoing not answered - | 'rejected' // Call was rejected/declined - | 'busy' // Remote party was busy - | 'answeredElsewhere' // Answered on another device - | 'missed' // No response to an incoming call - | 'error' // Call failed due to an error (e.g., network issue) - | 'canceled' // Call canceled before the remote party could answer - | 'restricted' // Call restricted (e.g., airplane mode) - | 'unknown'; // Unknown or unspecified disconnect reason - -type CallingExpiOSOptions = { - appName: string; - supportsVideo?: boolean; - maximumCallsPerCallGroup?: number; - maximumCallGroups?: number; - handleType?: 'generic' | 'number' | 'phone' | 'email'; -}; - -type CallingExpAndroidOptions = { - incomingChannel?: { - id: string; - name: string; - sound?: string; - vibration?: boolean; - }; - outgoingChannel?: { - id: string; - name: string; - sound?: string; - vibration?: boolean; - }; -}; - -type PermissionsResult = { - recordAudio: boolean; - postNotifications: boolean; -}; -``` - -## Troubleshooting - -### iOS - -- **Incoming call not showing**: Ensure `voip` background mode is enabled and VoIP push certificate is configured -- **CallKit errors**: Check that `appName` is set in setup options -- **Audio issues**: The module automatically configures the audio session, but ensure no conflicts with other audio libraries +## Main APIs -### Android +- `setup(options)` - required before any call action. +- `displayIncomingCall(callId, phoneNumber, callerName, hasVideo)`. +- `startCall(callId, phoneNumber, callerName, hasVideo)`. +- `setCurrentCallActive(callId)`. +- `updateDisplay(callId, phoneNumber, callerName, incoming)`. +- `endCallWithReason(callId, reason)`. +- `setMutedCall(callId, isMuted)`. +- `setOnHoldCall(callId, isOnHold)`. +- `addEventListener(eventName, callback)`. +- `getInitialEvents()` and `getInitialVoipEvents()`. +- `registerBackgroundTask(taskProvider)` / `startBackgroundTask()` / `stopBackgroundTask()` (Android). -- **Notifications not showing**: Check POST_NOTIFICATIONS permission on Android 13+ -- **Call not answered on tap**: Ensure `handleCallingIntent` is called in both `onCreate` and `onNewIntent` in your MainActivity +## Event names -## Contributing +Call events: -See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow. +- `answerCall` +- `endCall` +- `didDisplayIncomingCall` +- `didToggleHoldCallAction` +- `didPerformSetMutedCallAction` +- `didChangeAudioRoute` +- `didReceiveStartCallAction` +- `didActivateAudioSession` +- `didDeactivateAudioSession` -## License +VoIP events: -MIT +- `voipNotificationsRegistered` +- `voipNotificationReceived` ---- +## Notes -Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob) +- Import from `@stream-io/react-native-callingx`. +- iOS-only helpers: `registerVoipToken`, `fulfillAnswerCallAction`, `fulfillEndCallAction`. +- Android helpers: `canPostNotifications`, `isOngoingCallsEnabled`.