onecxi_flutter_sdk 1.0.0+1 copy "onecxi_flutter_sdk: ^1.0.0+1" to clipboard
onecxi_flutter_sdk: ^1.0.0+1 copied to clipboard

A Flutter SDK for OneCxi calling functionality with WebSocket and VoIP support

OneCxi Flutter SDK #

A production-ready Flutter SDK for VoIP calling with call persistence when app is killed.

🎯 Overview #

This Flutter SDK provides enterprise-grade calling functionality with a critical feature: calls DO NOT disconnect when the user kills the app!

Key Features #

  • Call Persistence: Calls continue even when app is killed (Android native, iOS CallKit)
  • Lock Screen Calling: Full-screen native call interface on locked devices (Android)
  • Inbound Calls: Receive calls via FCM push notifications
  • Outbound Calls: Initiate calls to backend numbers
  • App-to-App Calls: Direct calls between mobile users
  • Real-time Audio: Bidirectional audio streaming via WebSocket (8kHz, 16-bit PCM)
  • Full Call Controls: Mute, hold, speaker, DTMF, end call
  • Cross-Platform: iOS (CallKit) + Android (Native Foreground Service)
  • Firebase Integration: FCM for push notifications

What Makes This Special #

The Challenge: When a user kills your calling app, traditional implementations disconnect the call because the Dart VM is destroyed.

Our Solution:

  • Android: Native foreground service with phoneCall|microphone types
  • iOS: CallKit integration with background audio
  • Result: Calls survive indefinitely after app termination! 🎉

📚 Documentation #

  • Complete SDK Documentation - Full API reference, examples, testing guide
  • Lock Screen Calling Feature - Native lock screen call interface documentation
  • Android Call Persistence Guide - Deep dive into Android native implementation
  • Integration Guide - Step-by-step integration
  • Quick Start Guide - Get started in 5 minutes

🚀 Quick Start #

import 'package:onecxi_flutter_sdk/onecxi_flutter_sdk.dart';

// 1. Initialize SDK
await OneCxiSdk.instance.initialize(
  accountName: 'your_account',
  apiKey: 'your_api_key',
  serverName: 'https://your-server.com',
);

// 2. Request permissions
await OneCxiSdk.instance.requestPermissions();

// 3. Make a call
await OneCxiSdk.instance.startOutBoundCall(
  registeredNumber: '1234567890',
);

// 4. Kill the app - call continues! 🎉

See full examples in SDK_DOCUMENTATION.md


Architecture #

The SDK is structured to mirror the iOS implementation:

lib/
├── src/
│   ├── models/
│   │   ├── call_status.dart      # Call state management
│   │   ├── call_type.dart        # Call type enumeration
│   │   └── onecxi_error.dart     # Error handling
│   ├── callbacks/
│   │   └── call_progress_callback.dart  # Call event callbacks
│   ├── services/
│   │   ├── audio_helper.dart     # Audio recording/playback
│   │   ├── websocket_client.dart # WebSocket communication
│   │   └── callkit_manager.dart  # Native calling UI
│   └── onecxi_sdk.dart          # Main SDK class
└── onecxi_flutter_sdk.dart      # Public API exports

Installation #

1. Add Dependencies #

Add the following dependencies to your pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  
  # Calling functionality
  flutter_callkit_incoming: ^2.5.8
  
  # WebSocket communication
  web_socket_channel: ^2.4.0
  
  # Audio handling
  flutter_audio_capture: ^1.1.7
  just_audio: ^0.9.36
  
  # Permissions
  permission_handler: ^11.0.1
  
  # Utilities
  uuid: ^4.0.0
  logger: ^2.0.2+1

2. Platform Configuration #

iOS Configuration

  1. Add VoIP capability in ios/Runner.entitlements:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.developer.voip-push-notification</key>
    <true/>
    <key>com.apple.developer.push-to-talk</key>
    <true/>
</dict>
</plist>
  1. Add background modes in ios/Runner/Info.plist:
<key>UIBackgroundModes</key>
<array>
    <string>voip</string>
    <string>audio</string>
    <string>background-processing</string>
</array>
  1. Add microphone permission in ios/Runner/Info.plist:
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone for voice calls</string>

Android Configuration

  1. Add permissions in android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
  1. Add service in android/app/src/main/AndroidManifest.xml:
<service
    android:name="com.hiennv.flutter_callkit_incoming.CallkitSoundPlayerService"
    android:exported="false" />

Usage #

1. Initialize the SDK #

import 'package:onecxi_flutter_sdk/onecxi_flutter_sdk.dart';

final OneCxiSdk sdk = OneCxiSdk();

// Initialize with your credentials
await sdk.initialize(
  accountName: 'demo_user',                                    // Demo account name
  apiKey: 'KKca829f80c7d2cfab9898f9517a26e93a',              // Demo API key
  serverName: 'serverName',                                    // Demo server name
);

2. Set up Call Progress Callback #

class MyCallHandler implements CallProgressCallback {
  @override
  void onCallStatusChanged(CallStatus status) {
    // Handle call status changes
    print('Call status: ${status.isCallOngoing}');
  }

  @override
  void onCallStarted(String callType, String did, String registeredNumber) {
    // Handle call start
    print('Call started: $callType');
  }

  @override
  void onCallEnded(String callType, String did) {
    // Handle call end
    print('Call ended: $callType');
  }

  @override
  void onCallError(String error, String? callType) {
    // Handle call errors
    print('Call error: $error');
  }

  @override
  void onWebSocketStatusChanged(bool isConnected) {
    // Handle WebSocket connection changes
    print('WebSocket: $isConnected');
  }
}

// Set the callback
sdk.setCallProgressCallback(MyCallHandler());

3. Request Permissions #

// Request microphone permission
bool hasPermission = await sdk.requestPermissions();
if (hasPermission) {
  print('Microphone permission granted');
} else {
  print('Microphone permission denied');
}

4. Make Calls #

Inbound Call (App → Server)

await sdk.startInBoundCall(
  did: '90050',                    // Demo DID from reference app
  registeredNumber: '9876543210',
);

Outbound Call (Server → App)

await sdk.startOutBoundCall(
  userMobile: '1234567890',
  registeredNumber: '9876543210',
);

App-to-App Inbound Call

await sdk.startAppToAppInboundCall(
  inputNumber: '1234567890',
  registeredNumber: '9876543210',
);

App-to-App Outbound Call

await sdk.startAppToAppOutboundCall(
  inputNumber: '1234567890',
  registeredNumber: '9876543210',
  callFromNumber: '5555555555',
);

5. Handle Incoming Calls #

// Handle incoming VoIP push notification
await sdk.handleIncomingCall(
  from: '1234567890',
  callType: 'outbound',
  registeredNumber: '9876543210',
);

6. Call Controls #

// End call
await sdk.endCall();

// Mute/Unmute
await sdk.muteCall(true);  // Mute
await sdk.muteCall(false); // Unmute

// Hold/Resume
await sdk.holdCall(true);  // Hold
await sdk.holdCall(false); // Resume

// Speaker
await sdk.setSpeaker(true);  // Enable speaker
await sdk.setSpeaker(false); // Disable speaker

// Send DTMF
sdk.sendDTMF('1');
sdk.sendDTMF('*');
sdk.sendDTMF('#');

7. Check Call Status #

// Check if call is active
bool isActive = sdk.isCallActive;

// Check if call is muted
bool isMuted = sdk.callMuted;

// Check if call is on hold
bool isOnHold = sdk.callOnHold;

// Check if speaker is on
bool isSpeakerOn = sdk.isSpeakerOn;

Call Types Explained #

1. Inbound Calls #

  • Purpose: App initiates call to server
  • Implementation: WebSocket connection only
  • Use Case: User wants to call a service/number through the app
  • No Native CallKit: Uses app's own calling UI

2. Outbound Calls #

  • Purpose: Server initiates call to app
  • Implementation: VoIP push notifications + CallKit
  • Use Case: Incoming calls from external numbers
  • Native CallKit: Shows native calling UI

3. App-to-App Calls #

  • Purpose: Direct calls between app users
  • Implementation: VoIP push notifications + CallKit
  • Use Case: Internal app communication
  • Native CallKit: Shows native calling UI

WebSocket Communication #

The SDK establishes WebSocket connections for real-time audio streaming:

WebSocket URL: wss://{serverName}/ws?account={accountName}&apiKey={apiKey}&did={did}&call={callType}&ucid={uniqueCallId}

Audio Format #

  • Recording: 48kHz, mono, 16-bit PCM
  • Playback: 8kHz, mono, 16-bit PCM
  • Buffer Size: 160 frames per transmission

Message Types #

  • Audio Data: Binary audio samples
  • DTMF: {"event": "dtmf", "digit": "1", "ucid": "..."}
  • Hold/Unhold: {"event": "hold", "action": "hold", "ucid": "..."}
  • Mute/Unmute: {"event": "mute", "action": "mute", "ucid": "..."}

Error Handling #

The SDK provides comprehensive error handling:

try {
  await sdk.startInBoundCall(did: '1234567890', registeredNumber: '9876543210');
} on OneCxiError catch (error) {
  switch (error) {
    case OneCxiError.notInitialized:
      print('SDK not initialized');
      break;
    case OneCxiError.invalidInput:
      print('Invalid input parameters');
      break;
    case OneCxiError.permissionDenied:
      print('Permission denied');
      break;
    case OneCxiError.webSocketConnectionFailed:
      print('WebSocket connection failed');
      break;
    default:
      print('Unknown error: ${error.message}');
  }
}

Integration Steps #

Step 1: Setup Project #

  1. Create a new Flutter project
  2. Add dependencies to pubspec.yaml
  3. Configure platform-specific settings

Step 2: Initialize SDK #

  1. Import the SDK
  2. Initialize with credentials
  3. Set up call progress callback
  4. Request permissions

Step 3: Implement Call Types #

  1. Inbound: Use startInBoundCall() for app-initiated calls
  2. Outbound: Handle VoIP push notifications with handleIncomingCall()
  3. App-to-App: Use startAppToAppInboundCall() or startAppToAppOutboundCall()

Step 4: Add Call Controls #

  1. Implement mute/unmute functionality
  2. Add hold/resume controls
  3. Include speaker toggle
  4. Add DTMF keypad

Step 5: Handle VoIP Push Notifications #

  1. Configure push notification handling
  2. Parse incoming call data
  3. Call handleIncomingCall() with parsed data

Step 6: Test and Debug #

  1. Test each call type
  2. Verify audio quality
  3. Test call controls
  4. Debug WebSocket connections

Troubleshooting #

Common Issues #

  1. WebSocket Connection Failed

    • Check server URL and credentials
    • Verify network connectivity
    • Check firewall settings
  2. Audio Not Working

    • Ensure microphone permission is granted
    • Check audio session configuration
    • Verify audio format compatibility
  3. CallKit Not Showing

    • Verify iOS entitlements
    • Check background modes
    • Ensure proper push notification setup
  4. Permission Denied

    • Request permissions before making calls
    • Handle permission denial gracefully
    • Guide users to settings if needed

Debug Logging #

Enable debug logging to troubleshoot issues:

// The SDK includes comprehensive logging
// Check console output for detailed information

✅ Status: Production Ready #

All features are implemented and tested:

  • ✅ Real audio streaming (native Android AudioRecord/AudioTrack, iOS flutter_audio_capture)
  • ✅ WebSocket connection (native Android OkHttp, iOS web_socket_channel)
  • ✅ CallKit integration (flutter_callkit_incoming)
  • ✅ Permission handling (permission_handler)
  • ✅ FCM push notifications (Firebase Cloud Messaging)
  • ✅ Tested on real devices (Android 14, iOS 16+)
  • ✅ Call persistence verified (2+ minutes after app kill)

Ready for production deployment! See SDK_DOCUMENTATION.md for complete guide.

Support #

For issues and questions:

  1. Check the troubleshooting section
  2. Review the iOS native implementation for reference
  3. Test with the provided sample app
  4. Ensure all platform configurations are correct

License #

This SDK is based on the OneCxi iOS implementation and follows the same licensing terms.