Signed Call React Native SDK

Learn how to integrate Signed Call React Native SDK in your application to avail the Signed Call feature.

Overview

CleverTap enables in-app calls via its Signed Call React Native SDK, which means you can make and receive calls in any React Native application if the device has an internet connection and Signed Call React Native SDK. This document provides information about the following:

  • Signed Call React Native SDK integration
  • Manage Signed Calls from your React Native application

For more information about the feature, refer to Signed Call.

Prerequisites

The prerequisites for the Signed Call React Native SDK integration vary depending on the platforms as follows:

Android

  • SDK version 21 and above
  • Java version 8 and above
  • Application permissions for the following:

iOS

  • The Signed Call iOS framework is built using Swift-5 in Xcode v14. Signed Call iOS SDK requires iOS 12 as the minimum deployment target.
  • Application permissions for the following:
    • Microphone (Required)
    • Camera (Required)

πŸ“˜

Emulator Support

Emulator and Simulator support is available for voice calls, but voice transmission will not work.

Integration

The following are the four major steps for integrating Signed Call React Native SDK:

Integrate Signed Call React Native SDK

To integrate the Signed Call React Native SDK, add the CleverTap React Native SDK/Package to your project using Yarn or NPM as shown below:

yarn add @clevertap/clevertap-signed-call-react-native
npm install @clevertap/clevertap-signed-call-react-native

Set Up Signed Call Native SDKs

This process varies depending on the type of React Native application platform.

Android

To set up and integrate the Signed Call Android SDK:

  1. Include mavenCentral in your project-level build.gradle file as follows:
allprojects {
    repositories {
        mavenCentral()
    }
}
  1. Add the following code to the dependencies element of the build.gradle file of your application module:
//To integrate Signed Call Android SDK,
//consider using either v0.0.1, which targets Android 12, or v0.0.2 and later, which target Android 13
implementation "com.clevertap.android:clevertap-signedcall-sdk:0.0.5.5"

//To enable the socket-based signaling channel
implementation('io.socket:socket.io-client:2.1.0') {
        exclude group: 'org.json', module: 'json'
} 

//To load the image assets on the call screens
implementation 'com.github.bumptech.glide:glide:4.12.0'

//To process the incoming call push for the receiver
implementation 'androidx.work:work-runtime:2.7.1'

//To build a responsive UI for the call screens
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
  1. The Signed Call Android SDK uses CleverTap Android SDK for analytics. The Signed Call Android SDK requires an active CleverTap instance as a parameter during the SDK initialization. To integrate the CleverTap Android SDK, refer to the CleverTap Android SDK Integration.

    πŸ“˜

    Signed Call React Native SDK Compatibility

    The Signed Call React Native SDK v0.0.5.1 is compatible with Signed Call Android SDK 0.0.5.5 or higher and CleverTap Android SDK v6.2.0 or higher. For more details, refer to the Signed Call React Native SDK ChangeLog.

  2. The Signed Call Android SDK uses FCM dependency to fetch the FCM token of the device required for successful SDK initialization. The Signed Call then uses this FCM token to enable calls via the FCM-based call Routing Channel.
    Add the following code to include the FCM dependency in the application module's dependency element:
implementation 'com.google.firebase:firebase-messaging:21.0.0'

πŸ“˜

FCM Version

The minimum supported version of FCM must be v21.0.0 or higher to integrate with your Android platform.

iOS

To set up and integrate the Signed Call iOS SDK:

  1. Add the pod spec repo as a source and add the source, pre_install and post_install script to your Podfile as shown below:
#Add source before App target

source 'https://github.com/CleverTap/podspecs.git'
source 'https://github.com/CocoaPods/Specs.git'

#Add these pods in your App target
target 'YOUR_TARGET_NAME' do  
 use_frameworks!
 pod 'clevertap-signed-call-react-native', :path => '../..'
 pod 'CleverTap-iOS-SDK', '4.2.0', modular_headers: true
 pod 'CleverTap-SignedCall-SDK'
 pod 'Permission-Microphone', :path => '../node_modules/react-native-permissions/ios/Microphone/Permission-Microphone.podspec'
 pod 'Permission-Camera', :path => '../node_modules/react-native-permissions/ios/Camera/Permission-Camera.podspec'
end

#pre_install script as we are using `use_frameworks!`
pre_install do |installer|
    Pod::Installer::Xcode::TargetValidator.send(:define_method, :verify_no_static_framework_transitive_dependencies) {}
    
    installer.pod_targets.each do |pod|
      if pod.name.eql?('RNPermissions') || pod.name.start_with?('Permission-')
        def pod.build_type;
        Pod::BuildType.static_library
      end
    end
  end
end

#post_install script
post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
    end
  end
  
  installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result|
    target_installation_result.native_target.build_configurations.each do |config|
      # For third party modules who have React-bridging dependency to search correct headers
      config.build_settings['HEADER_SEARCH_PATHS'] ||= '$(inherited) '
      config.build_settings['HEADER_SEARCH_PATHS'] << '"$(PODS_ROOT)/Headers/Private/React-bridging/react/bridging" '
      config.build_settings['HEADER_SEARCH_PATHS'] << '"$(PODS_CONFIGURATION_BUILD_DIR)/React-bridging/react_bridging.framework/Headers" '
    end
  end
  react_native_post_install(installer)
  __apply_Xcode_12_5_M1_post_install_workaround(installer)
end
  1. Update your Podfile, and run pod install --repo-update in your terminal. For more information, refer to Pod Setup
  2. The Signed Call iOS SDK uses CleverTap SDK for analytics. The Signed Call iOS SDK requires an active CleverTap instance as a parameter during the SDK initialization.

πŸ“˜

Compatibility

The Signed Call iOS SDK integrates with CleverTap SDK v4.1.6 or higher.

  1. To set up your Xcode project, refer to Set Up Xcode Project.
  2. To set up an outgoing tone, refer to Set Up and Outgoing Tone.
  3. To configure the quick launch button on the CallKit screen, refer to Configure Quick Launch Button.

πŸ“˜

Icon Image File Specifications

The icon must be square with a side length of 40 points. The image's alpha channel creates a white mask image used in the native CallKit screen UI for the button, which helps switch between the native CallKit screen and the application's calling screen.

Integrate the Signed Call iOS SDK

Integrate the Signed Call iOS SDK and CleverTap iOS SDK as follow:

  1. Open your AppDelegate file and import both, CleverTap and Signed Call SDKs as shown below:
@import CleverTapSDK;
@import SignedCallSDK;
import CleverTapSDK
import SignedCallSDK

πŸ“˜

Note

You must use @import instead of #import in the AppDelegate Objective-C class when using use_modular_headers! in the podfile.

  1. Call the registerVoIP function to generate a VoIP token and set pushRegistryDelegate in your AppDelegate file.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        
  //React Native setup code
  //...
  
  CleverTap.setDebugLevel(CleverTapLogLevel.off.rawValue)
  CleverTap.autoIntegrate()
  SignedCall.cleverTapInstance = CleverTap.sharedInstance()
  [SignedCall registerVoIPWithRootView:self.window.rootViewController appName:@"Sample App"];
     
  //...
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  //React Native setup code
  //...
  CleverTap.setDebugLevel(CleverTapLogLevel.off.rawValue)
  CleverTap.autoIntegrate()
  SignedCall.isLoggingEnabled = true
  SignedCall.cleverTapInstance = CleverTap.sharedInstance()
        
  guard let rootView = self.window?.rootViewController else {
       return true
  }
  SignedCall.registerVoIP(withRootView: rootView)
  //...
}

The registerVoIP function expects the following parameters:

ParameterDescriptionTypeNotes
rootViewThe view controller on which the call screen displays.UIViewControllerRequired
appNameThe application name you want to display on the CallKit incoming screen.StringOptional

Initialize Signed Call React Native SDK

To initialize Signed Call React Native SDK in your JavaScript or TypeScript code, call the init method on the SignedCall reference as shown below:

Method Declaration

static initialize(initProperties: object): Promise<SignedCallResponse>

This method returns a Promise object. Use then() method to get the results.

Usage

import {
  SignedCall,
  SignedCallResponse,
} from '@clevertap/clevertap-signed-call-react-native';

SignedCall.initialize(initProperties)
  .then((response: SignedCallResponse) => {
    if (response.isSuccessful) {
      console.log('Signed Call SDK initialized: ', response);
    } else {
      const error = response.error;
      const errorCode = error?.errorCode;
      const errorMessage = error?.errorMessage;
      const errorDescription = error?.errorDescription;
      console.log(
            'Signed Call initialization failed: \n' +
            'error-code: ' + errorCode + '\n' +
            'error-message: ' + errorMessage + '\n' +
            'error-description: ' + errorDescription
      );
    }
  })
  .catch((e: any) => {
    console.error(e);
  });
import {
  SignedCall,
  SignedCallResponse,
} from '@clevertap/clevertap-signed-call-react-native';

SignedCall.initialize(initProperties)
  .then(response => {
    if (response.isSuccessful) {
      console.log('Signed Call SDK initialized: ', response);
    } else {
      const error = response.error;
      const errorCode = error?.errorCode;
      const errorMessage = error?.errorMessage;
      const errorDescription = error?.errorDescription;
      console.log(
            'Signed Call initialization failed: \n' +
            'error-code: ' + errorCode + '\n' +
            'error-message: ' + errorMessage + '\n' +
            'error-description: ' + errorDescription
      );
    }
  })
  .catch(e => {
    console.error(e);
  });

The initProperties is a Map object with the following properties:

PropertiesDescriptionTypeComments
accountIdThe unique identity of the client's account. It is available from the CleverTap dashboard.StringRequired
apiKeyThe unique identity of the client's account. It is available from the CleverTap dashboard.StringRequired
cuidThe unique identity of the user.StringRequired
overrideDefaultBrandingIt overrides the call screen’s branding set from the dashboard.
To know more, refer to its usage.
ObjectOptional

The default branding is the one you set from the dashboard.
productionIt enables the VoIP push based on the build type of the iOS application.
To know more, refer to its usage.
BooleanRequired (iOS)
allowPersistSocketConnectionIt controls the persistence of the socket connection that SDK uses to initiate or receive the calls.
To know more, refer to its usage.
BooleanRequired (Android)
notificationPermissionRequiredIt indicates whether notification permission is required for the SDK initialization on Android 13 and onwards. To know more, refer to its usage .BooleanOptional
(Android)

The default value is true.
promptPushPrimerIt enables the Push Primer to support Android 13 changes for the Runtime Notification Permission. For more information, refer to Android 13 Changes.ObjectOptional (Android)
promptReceiverReadPhoneStatePermissionWhen the receiver answers the call, it prompts the receiver with Read Phone State permission.
The SDK uses Read Phone State permission to enable busy handling for PSTN calls.
For more information, refer to Read Phone State.
BooleanOptional (Android)

The default value is false.
missedCallActionsIt configures the action buttons for the missed call notification.

For more information, refer to missedCallActions.
ObjectOptional (Android)

No action buttons are displayed on a missed call notification without this parameter.

A maximum of three action buttons can be configured for the missed call notification, where each entry in the map object represents an action button.
swipeOffBehaviourInForegroundServiceIt defines the SDK behaviour during an active call when the user swipe off the app.

To know more, refer to its usage .

πŸ“˜

CUID Validation Rules

The following are the validation rules for cuid:

  • Starting from Signed Call React Native SDK v0.0.5, the characters must range between 5 to 50. For earlier SDK versions, limit characters to less than 15.
  • Start with either alphabet or a number.
  • The name is case-sensitive, and only '_' is allowed as a special character.
  • Cannot be of type number-number, i.e. a number followed by a special character, which is again followed by another number. For example, org_25 is allowed, but 91_8899555 is not permitted.
  • Must be unique for every user.
  • Must be unique for every device to allow multiple logins for the user from different devices. In such cases, the user will have multiple cuid's.

The syntax for initProperties is as follows:

import { Platform } from 'react-native';

let initProperties: { [k: string]: any } = {
      accountId: <string / required>,
      apiKey: <string / required>,
      cuid: <string / required>,
}
    
if (Platform.OS === 'android') {
      initProperties.allowPersistSocketConnection = <boolean / required>;
      initProperties.promptReceiverReadPhoneStatePermission = <boolean / optional>;
      initProperties.missedCallActions = <object / optional>;
}
    
if (Platform.OS === 'ios') {
      initProperties.production = <boolean / required>;
}
import { Platform } from 'react-native';

let initProperties = {
      accountId: <string / required>,
      apiKey: <string / required>,
      cuid: <string / required>,
}
    
if (Platform.OS === 'android') {
      initProperties.allowPersistSocketConnection = <boolean / required>;
      initProperties.promptReceiverReadPhoneStatePermission = <boolean / optional>;
      initProperties.missedCallActions = <object / optional>;
}
    
if (Platform.OS === 'ios') {
      initProperties.production = <boolean / required>;
}

overrideDefaultBranding (All Platforms)

CleverTap dashboard provides a branding tool to alter the look and feel of the call screens. If you have multiple applications to integrate with the Signed Call SDK, all those applications will share the same branding you have set from the CleverTap dashboard. By overriding the dashboard's call screen branding, you can have different branding for each application.

Use an optional overrideDefaultBranding parameter of the object type inside the initProperties to override the dashboard branding for call screens.

import {
  SignedCall,
  SignedCallResponse,
} from '@clevertap/clevertap-signed-call-react-native';

let callScreenBranding = {
  bgColor: '<hex color code>',    //The background color of the call screens
  fontColor: '<hex color code>',  //The color of the text displayed on the call screens
  logoUrl: '<https url>',         //The image URL that renders on the call screens.
  buttonTheme: 'light' or "dark", //The theme of the control buttons shown on the ongoing call screen(i.e. Mute, Speaker and Bluetooth)
};

let initProperties = {
  ....
  overrideDefaultBranding: callScreenBranding,
  ....
};

SignedCall.initialize(initProperties)
  .then((response: SignedCallResponse) => {
    if (response.isSuccessful) {
      console.log('Signed Call SDK initialized: ', response);
    } else {
      console.log('Signed Call initialization failed: ', response.error);
    }
 })
  .catch((e: any) => {
    console.error(e);
  });
import {
  SignedCall,
  SignedCallResponse,
} from '@clevertap/clevertap-signed-call-react-native';

let callScreenBranding = {
  bgColor: '<hex color code>',    //The background color of the call screens
  fontColor: '<hex color code>',  //The color of the text displayed on the call screens
  logoUrl: '<https url>',         //The image URL that renders on the call screens.
  buttonTheme: 'light' or "dark", //The theme of the control buttons shown on the ongoing call screen(i.e. Mute, Speaker and Bluetooth)
};

let initProperties = {
  ....
  overrideDefaultBranding: callScreenBranding,
  ....
};

SignedCall.initialize(initProperties)
  .then(response => {
    if (response.isSuccessful) {
      console.log('Signed Call SDK initialized: ', response);
    } else {
      console.log('Signed Call initialization failed: ', response.error);
    }
  })
  .catch(e => {
    console.error(e);
  });

production (iOS Platform)

The Signed Call React Native SDK uses the production field in iOS to set up the production or development environment for VoIP push support.

πŸ“˜

Validation

The production must be true if you build an application for production and false if you build an application for development. The default value is false.

allowPersistSocketConnection (Android Platform)

The socket connection plays a crucial role in processing the call request to initiate a call and receive calls on the socket channel. Android OS imposes several battery restrictions that lead to issues in maintaining a persistent socket connection. To overcome this issue, Signed Call React Native SDK expects allowPersistSocketConnection flag, a required boolean type parameter passed inside the initProperties.

The following is the Signed Call React Native SDK behavior for Android platform:

ValueSocket ConnectionSigned Call React Native SDK
truePersistentFor Android platform, the Signed Call React Native SDK uses a background service to keep the socket connection persistent in the foreground and background states of the application.
falseNon-persistentThe Signed Call React Native SDK does not use the background service for the socket connection; hence the socket connection may be inconsistent under battery restriction scenarios.

πŸ“˜

Recommended Settings for Android 11 and Above

For Android 11 and above, if the socket connection is persistent, the application might show a system-generated notification to the user that the application is draining the device's battery. We recommend setting the value as false and keeping the socket connection non-persistent for transactional businesses.

Android 13 Changes

All applications having Android 13 and above must request Runtime Notification Permission from the user before sending the notifications.

For android platform, Signed Call React Native SDK utilizes both local and remote notifications during VoIP calls. Therefore, notification permission is required from the user to initialize the Signed Call Flutter SDK.

πŸ“˜

Signed Call React Native SDK v0.0.2 Supports Android 13

If you want to increase the target API of your application to 33 or higher, you must upgrade the following:

  • Signed Call React Native SDK to version 0.0.2 or higher
  • CleverTap React Native SDK to version 1.0.0 or higher

Push Primer for Push Notification Permission

The Push Primer is a local In-App notification that educates users about the context of the notifications before requesting the notification permission.

To initialize the Signed Call React Native SDK for Android 13 and above, you can enable the Push Primer using any of the following:

Enable Push Primer via CleverTap React Native SDK

CleverTap React Native SDK v1.0.0 and above supports Push Primer. For more information, refer to CleverTap React Native Push Primer.

Enable Push Primer via Signed Call React Native SDK

The Signed Call React Native SDK enables you to display Push Primer using an optional promptPushPrimer parameter of Object type inside the initProperties. It ensures that the initialization happens after the notification permission is granted using Push Primer.

To configure the Push Primer:

  1. Create a Push Primer configuration using the In-App campaign's Half-Interstitial or Alert template.
//Creates push primer config using Half-Interstitial template
const pushPrimerConfig = {
  inAppType: 'half-interstitial',
  titleText: 'Get Notified',
  messageText:'Please enable notifications on your device to use Push Notifications.',
  followDeviceOrientation: true,
  positiveBtnText: 'Allow',
  negativeBtnText: 'Cancel',
  backgroundColor: '#FFFFFF',
  btnBorderColor: '#0000FF',
  titleTextColor: '#0000FF',
  messageTextColor: '#000000',
  btnTextColor: '#FFFFFF',
  btnBackgroundColor: '#0000FF',
  btnBorderRadius: '2',
  fallbackToSettings: true, //Setting this parameter to true will open an in-App to redirect you to Mobile's OS settings page.
};

//Creates push primer config using Alert template
const pushPrimerConfig = {
  inAppType: 'alert',
  titleText: 'Get Notified',
  messageText: 'Enable Notification permission',
  followDeviceOrientation: true,
  positiveBtnText: 'Allow',
  negativeBtnText: 'Cancel',
  fallbackToSettings: true, //Setting this parameter to true will open an in-App to redirect you to Mobile's OS settings page.
};
//Creates push primer config using Half-Interstitial template
const pushPrimerConfig = {
  inAppType: 'half-interstitial',
  titleText: 'Get Notified',
  messageText:'Please enable notifications on your device to use Push Notifications.',
  followDeviceOrientation: true,
  positiveBtnText: 'Allow',
  negativeBtnText: 'Cancel',
  backgroundColor: '#FFFFFF',
  btnBorderColor: '#0000FF',
  titleTextColor: '#0000FF',
  messageTextColor: '#000000',
  btnTextColor: '#FFFFFF',
  btnBackgroundColor: '#0000FF',
  btnBorderRadius: '2',
  fallbackToSettings: true, //Setting this parameter to true will open an in-App to redirect you to Mobile's OS settings page.
};

//Creates push primer config using Alert template
const pushPrimerConfig = {
  inAppType: 'alert',
  titleText: 'Get Notified',
  messageText: 'Enable Notification permission',
  followDeviceOrientation: true,
  positiveBtnText: 'Allow',
  negativeBtnText: 'Cancel',
  fallbackToSettings: true, //Setting this parameter to true will open an in-App to redirect you to Mobile's OS settings page.
};
  1. Pass the Push Primer configuration inside the promptPushPrimer parameter of the initProperties . It displays the Push Primer during the SDK initialization.
import {
  SignedCall,
  SignedCallResponse,
} from '@clevertap/clevertap-signed-call-react-native';

let initProperties = {
  ....
  promptPushPrimer: pushPrimerConfig,
  ....
};

SignedCall.initialize(initProperties)
  .then((response: SignedCallResponse) => {
    if (response.isSuccessful) {
      console.log('Signed Call SDK initialized: ', response);
    } else {
      console.log('Signed Call initialization failed: ', response.error);
    }
  })
  .catch((e: any) => {
    console.error(e);
  });
import {
  SignedCall,
  SignedCallResponse,
} from '@clevertap/clevertap-signed-call-react-native';

let initProperties = {
  ....
  promptPushPrimer: pushPrimerConfig,
  ....
};

SignedCall.initialize(initProperties)
  .then(response => {
    if (response.isSuccessful) {
      console.log('Signed Call SDK initialized: ', response);
    } else {
      console.log('Signed Call initialization failed: ', response.error);
    }
  })
  .catch(e => {
    console.error(e);
  });

πŸ“˜

Note

  • The above configuration enables the Push Primer only if the device and application both target Android 13 (API level 33) or higher.
  • During the SDK initialization, Signed Call React Native SDK registers a listener to obtain the result of the push notification permission request. After registration, this listener continues monitoring the permission result, even if the Push Primer prompt is displayed from the CleverTap Flutter SDK.
  • Inf the notification permission is denied, the Signed Call React Native SDK returns an exception within the promise object that is associated to the SignedCall.initialize(..) method.

Configure Notification Permission as Optional (Android Platform)

πŸ“˜

Expected SDK Behaviour without Notification Permission

  • Avoid making notification permissions optional. Without this permission, the SDK can only display the call screen on Android 13 and above when the app is in the foreground. Otherwise, incoming calls are automatically declined, triggering the CallEvent.DeclinedDueToNotificationsDisabled event in the SignedCall.SignedCallOnCallStatusChanged listener.
  • If you decide to set notification permissions as non-required, implementing the SignedCall.SignedCallOnCallStatusChanged listener is essential.

Starting with Android 13, the Signed Call React Native SDK requires notification permission to display call notifications, allowing users to interact with and return to calls.

By default, the SDK needs this permission for initialization. If it is not granted, the SDK will return an exception within the promise object linked to the SignedCall.initialize(..) method.

To make notification permission optional during SDK initialization, set the notificationPermissionRequired parameter of the initProperties to false, as shown below:

import {
  SignedCall,
  SignedCallResponse,
} from '@clevertap/clevertap-signed-call-react-native';

let initProperties = {
  ....
  notificationPermissionRequired: <true/false>,
  ....
};

SignedCall.initialize(initProperties)
  .then((response: SignedCallResponse) => {
    if (response.isSuccessful) {
      console.log('Signed Call SDK initialized: ', response);
    } else {
      console.log('Signed Call initialization failed: ', response.error);
    }
  })
  .catch((e: any) => {
    console.error(e);
  });
import {
  SignedCall,
  SignedCallResponse,
} from '@clevertap/clevertap-signed-call-react-native';

let initProperties = {
  ....
  notificationPermissionRequired: <true/false>,
  ....
};

SignedCall.initialize(initProperties)
  .then(response => {
    if (response.isSuccessful) {
      console.log('Signed Call SDK initialized: ', response);
    } else {
      console.log('Signed Call initialization failed: ', response.error);
    }
  })
  .catch(e => {
    console.error(e);
  });

Manage Permission

Following are the permissions required for the Signed Call React Native SDK integration:

Microphone (All Platforms)

This is a required permission. The Signed Call React Native SDK requires microphone permission to exchange voices during the call. At the receiver's end, the Signed Call React Native SDK asks for microphone permission and handles it accordingly when the receiver answers the call. If the receiver denies microphone permission, the Signed Call React Native SDK declines the call. We recommend you add the required handling to request the microphone permission before initiating a call.

Camera (iOS Platform)

This is a required permission. The Signed Call React Native SDK requires camera permission entry in theinfo.plist to meet CallKit policies to initiate and receive VOIP calls. We recommend you add the NSCameraUsageDescription key in info.plist .

Read Phone State (Android Platform)

This is optional permission. The Signed Call React Native SDK uses this permission to enable busy handling for Public Switched Telephone Network (PSTN) calls. This permission determines if the receiver is available or engaged on a PSTN call. We recommend you add the required handling to request the Read Phone State permission before initiating a call.

Use the promptReceiverReadPhoneStatePermission parameter of the boolean type inside the initProperties as shown below to allow the Signed Call React Native SDK to prompt the read phone state permission at the receiver's end when the receiver answers the call.

import {
  SignedCall,
  SignedCallResponse,
} from '@clevertap/clevertap-signed-call-react-native';

let initProperties = {
  ....
  promptReceiverReadPhoneStatePermission: true/false,
  ....
};

SignedCall.initialize(initProperties)
  .then((response: SignedCallResponse) => {
    if (response.isSuccessful) {
      console.log('Signed Call SDK initialized: ', response);
    } else {
      console.log('Signed Call initialization failed: ', response.error);
    }
  })
  .catch((e: any) => {
    console.error(e);
  });
import {
  SignedCall,
  SignedCallResponse,
} from '@clevertap/clevertap-signed-call-react-native';

let initProperties = {
  ....
  promptReceiverReadPhoneStatePermission: true/false,
  ....
};

SignedCall.initialize(initProperties)
  .then(response => {
    if (response.isSuccessful) {
      console.log('Signed Call SDK initialized: ', response);
    } else {
      console.log('Signed Call initialization failed: ', response.error);
    }
  })
  .catch(e => {
    console.error(e);
  });

Logout the Signed Call React Native SDK

When the Signed Call React Native SDK initializes, it maintains the init configuration in a local session. Use the logout() method to invalidate the active session and disable the Signed Call functionality (make and receive a call). To enable it again, repeat the steps listed under Initialize Signed Call React Native SDK.

SignedCall.logout();
SignedCall.logout();

Make the Signed Call

To make a Signed Call in your JavaScript or TypeScript code, use the call method on the SignedCall reference as shown below:

Method Declaration

static call(
    receiverCuid: string,
    callContext: string,
    callProperties?: object | undefined
  ): Promise<SignedCallResponse>

This method returns a Promise object. Use then() method to get the results.

Usage

import {
  SignedCall,
  SignedCallResponse,
} from '@clevertap/clevertap-signed-call-react-native';

//following callProperties object is an optional parameter
let callProperties = {
      initiator_image: '<https url / optional>',
      receiver_image: '<https url / optional>',
};

SignedCall.call(receiverCuid, callContext, callProperties)
      .then((response: SignedCallResponse) => {
        if (response.isSuccessful) {
          console.log('Signed call is placed successfully', response);
        } else {
          const error = response.error;
          const errorCode = error?.errorCode;
          const errorMessage = error?.errorMessage;
          const errorDescription = error?.errorDescription;
          console.log(
            'Signed Call initialization failed: \n' +
            'error-code: ' + errorCode + '\n' +
            'error-message: ' + errorMessage + '\n' +
            'error-description: ' + errorDescription
          );
        }
      })
      .catch((e: any) => {
        console.error(e);
      });
import {
  SignedCall,
  SignedCallResponse,
} from '@clevertap/clevertap-signed-call-react-native';

//following callProperties object is an optional parameter
let callProperties = {
      initiator_image: '<https url / optional>',
      receiver_image: '<https url / optional>',
};

SignedCall.call(receiverCuid, callContext)
      .then(response => {
        if (response.isSuccessful) {
          console.log('Signed call is placed successfully', response);
        } else {
          const error = response.error;
          const errorCode = error?.errorCode;
          const errorMessage = error?.errorMessage;
          const errorDescription = error?.errorDescription;
          console.log(
            'Signed Call initialization failed: \n' +
            'error-code: ' + errorCode + '\n' +
            'error-message: ' + errorMessage + '\n' +
            'error-description: ' + errorDescription
          );
        }
      })
      .catch(e => {
        console.error(e);
      });

The parameters to make a Signed Call are as follows:

ParameterDescriptionTypeComments
receiverCuidIt is the receiver's cuid.StringRequired
contextCallIt specifies the context of the call. For example, Delivery Partner is calling, the Driver is calling, Agent is calling, and so on.
The parameter must include alphanumeric characters, and its length must not exceed 64 characters.
StringRequired
callOptionsIt is an object with the following properties:

1. receiver_image (string): URL that displays the receiver's image to the initiator of the call (optional).

2. initiator_image (string): URL that displays the initiator's image to the receiver of the call (optional).
ObjectOptional

Setup to Receive a Signed Call

Signed Call uses the following routing channels to receive Signed Calls at the receiver's end:

Socket Channel (All Platforms)

It is a primary routing channel. Signed Call React Native SDK requires a successful initialization to receive a call on the socket channel.

FCM Channel (Android Platform)

It is a secondary or fallback routing channel used when the receiver is not connected to the primary routing channel (Socket).

πŸ“˜

FCM Version

The minimum supported version of FCM must be v21.0.0 or higher to integrate with your Android platform.

To enable the FCM channel, follow the steps below:

  1. Add your FCM Server Key to the Signed Call section of the CleverTap dashboard. Ignore it if you have already added it.
  2. Allow CleverTap Android SDK to process the FCM push of VoIP call by adding the following entries to your AndroidManifest.xml file.
<application>
         ....
         ....
        <service android:name="com.clevertap.android.sdk.pushnotification.fcm.FcmMessageListenerService"
                 android:exported="true">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>
        ....  
 </application>

  1. Add the following code to your Application class:
CleverTapAPI.setSignedCallNotificationHandler(new SignedCallNotificationHandler());
CleverTapAPI.setSignedCallNotificationHandler(SignedCallNotificationHandler())

The setup is now complete for receiving calls via the FCM channel.

APNs Channel (iOS Platform)

It is a secondary or fallback routing channel for the iOS platform, used when the receiver is not connected to the primary routing channel (Socket).

Set up the APNs in the Signed Call section of the CleverTap dashboard to start receiving VoIP notifications.

Close Socket Connection (All Platforms)

The Signed Call React Native SDK uses the socket connection to signal VoIP calls from the initiator to the receiver. After successful SDK initialization, the socket connection to initiate or receive VoIP calls opens.

If the socket is left open for a longer period, the application might drain the device's battery. In the case of Android, the application might also show a system-generated notification to the user that the application is draining the device's battery. Therefore, we recommend disconnecting the socket after all expected/pending transactions are over.

To close the socket connection, use the following method as per your business use case:

SignedCall.disconnectSignallingSocket();
SignedCall.disconnectSignallingSocket();

Configure Swipe Off Behaviour (Android Platform)

By default, the Signed Call React Native SDK terminates a call when the user swipes off the call screen from the recent task screen. However, if your app runs a foreground service to maintain operation even after the app's swipe-off, you can modify this default behavior. The SDK allows you to override the default behavior and persist the call within the foreground service managed by your application.

To change the swipe-off behavior, use the notificationPermissionRequired parameter of the initProperties to pass the enum constant SCSwipeOffBehaviour.PersistCall to ensure the call remains persistent in a foreground service upon swiping off. The default value is SCSwipeOffBehaviour.EndCall

To change the default swipe off behavior, use the following snippet:

import {
  SignedCall,
  SignedCallResponse,
} from '@clevertap/clevertap-signed-call-react-native';

let initProperties = {
  ....
  swipeOffBehaviourInForegroundService: <SCSwipeOffBehaviour.PersistCall or SCSwipeOffBehaviour.EndCall>,
  ....
};

SignedCall.initialize(initProperties)
  .then((response: SignedCallResponse) => {
    if (response.isSuccessful) {
      console.log('Signed Call SDK initialized: ', response);
    } else {
      console.log('Signed Call initialization failed: ', response.error);
    }
  })
  .catch((e: any) => {
    console.error(e);
  });
import {
  SignedCall,
  SignedCallResponse,
} from '@clevertap/clevertap-signed-call-react-native';

let initProperties = {
  ....
  swipeOffBehaviourInForegroundService: <SCSwipeOffBehaviour.PersistCall or SCSwipeOffBehaviour.EndCall>,
  ....
};

SignedCall.initialize(initProperties)
  .then(response => {
    if (response.isSuccessful) {
      console.log('Signed Call SDK initialized: ', response);
    } else {
      console.log('Signed Call initialization failed: ', response.error);
    }
  })
  .catch(e => {
    console.error(e);
  });

Handle Call Hangup (All Platforms)

The call hangup functionality is user-driven, and ending the call depends on the user. For example, if one of the users in a call clicks the call hangup button from the ongoing call screen, the Signed Call React Native SDK internally manages the call hangup functionality to end the call.

In the case of a metered call, when a business wants to end the call after a specific duration, you must maintain a timer in the application and use the following method to terminate the call when the timer ends:

SignedCall.hangup();
SignedCall.hangup();

Handle Call Events (All Platforms)

To define custom handling for call-related events, you must register the SignedCall.SignedCallOnCallStatusChanged listener. This listener provides an object - CallEventResult containing information about the call.

Refer to the below code to define custom handling for distinct call events:

import {
  SignedCall,
  CallDirection,
  CallEvent,
  CallEventResult,
} from '@clevertap/clevertap-signed-call-react-native';

//To start observing the changes in a call states.
SignedCall.addListener(
      SignedCall.SignedCallOnCallStatusChanged,
      (result: CallEventResult) => {
        console.log('SignedCallOnCallStatusChanged', result);

        if (result.direction === CallDirection.Incoming) {
          console.log('Call direction is Incoming!');
        } else if (result.direction === CallDirection.Outgoing) {
          console.log('Call direction is Outgoing!');
        }

        if (result.callEvent === CallEvent.CallIsPlaced) {
          // Indicates that the call is successfully placed
        } else if (result.callEvent === CallEvent.Cancelled) {
          // Indicates that the call is cancelled from the initiator's end
        } else if (result.callEvent === CallEvent.CancelledDueToRingTimeout) {
          // [Specific to Android-Platform]
          // Indicates that the call is cancelled due to a ring timeout(35 secs)
        } else if (result.callEvent === CallEvent.Declined) {
          // Indicates that the call is declined from the receiver's end
        } else if (result.callEvent === CallEvent.Missed) {
          // Indicates that the call is missed at the receiver's end
        } else if (result.callEvent === CallEvent.Answered) {
          // Indicates that the call is picked up by the receiver
        } else if (result.callEvent === CallEvent.CallInProgress) {
          // Indicates that the connection to the receiver is established and the audio transfer begins at this stateaa
        } else if (result.callEvent === CallEvent.Ended) {
          // Indicates that the call has been ended
        } else if (result.callEvent === CallEvent.ReceiverBusyOnAnotherCall) {
          // Indicates that the receiver is already busy on another call
        }  else if (result.callEvent === CallEvent.DeclinedDueToBusyOnVoIP) {
          // [Specific to Android-Platform]
          // Indicates that the receiver is busy on VoIP call
        } else if (result.callEvent === CallEvent.DeclinedDueToBusyOnPSTN) {
          // [Specific to Android-Platform]
          // Indicates that the receiver is busy on PSTN call
        }
        else if (result.callEvent === CallEvent.DeclinedDueToLoggedOutCuid) {
          // Indicates that the call is declined due to the receiver being logged out with the specific CUID
        } else if (
          result.callEvent === CallEvent.DeclinedDueToNotificationsDisabled
        ) {
          // [Specific to Android-Platform]
          // Indicates that the call is declined due to the notifications are disabled at the receiver's end
        } else if (
          result.callEvent ===
          CallEvent.DeclinedDueToMicrophonePermissionsNotGranted
        ) {
          // Indicates that the microphone permission is not granted for the call
        }
      }
    );

//To stop observing the changes in a call state.
SignedCall.removeListener(SignedCall.SignedCallOnCallStatusChanged);
import {
  SignedCall,
  CallEvent,
  CallDirection,
} from '@clevertap/clevertap-signed-call-react-native';

//To start observing the changes in a call states.
SignedCall.addListener(SignedCall.SignedCallOnCallStatusChanged, (result) => {
    console.log('SignedCallOnCallStatusChanged', result);

    if (result.direction === CallDirection.Incoming) {
      console.log('Call direction is Incoming!');
    } else if (result.direction === CallDirection.Outgoing) {
      console.log('Call direction is Outgoing!');
    }

    if (result.callEvent === CallEvent.CallIsPlaced) {
      // Indicates that the call is successfully placed
    } else if (result.callEvent === CallEvent.Cancelled) {
      // Indicates that the call is cancelled from the initiator's end
    } else if (result.callEvent === CallEvent.CancelledDueToRingTimeout) {
      // [Specific to Android-Platform]
      // Indicates that the call is cancelled due to a ring timeout(35 secs)
    } else if (result.callEvent === CallEvent.Declined) {
      // Indicates that the call is declined from the receiver's end
    } else if (result.callEvent === CallEvent.Missed) {
      // Indicates that the call is missed at the receiver's end
    } else if (result.callEvent === CallEvent.Answered) {
      // Indicates that the call is picked up by the receiver
    } else if (result.callEvent === CallEvent.CallInProgress) {
      // Indicates that the connection to the receiver is established, and the audio transfer begins at this stateaa
    } else if (result.callEvent === CallEvent.Ended) {
      // Indicates that the call has been ended
    } else if (result.callEvent === CallEvent.ReceiverBusyOnAnotherCall) {
      // Indicates that the receiver is already busy on another call
    } else if (result.callEvent === CallEvent.DeclinedDueToBusyOnVoIP) {
      // [Specific to Android-Platform]
      // Indicates that the receiver is busy on VoIP call
    } else if (result.callEvent === CallEvent.DeclinedDueToBusyOnPSTN) {
       // [Specific to Android-Platform]
       // Indicates that the receiver is busy on PSTN call
    } else if (result.callEvent === CallEvent.DeclinedDueToLoggedOutCuid) {
      // Indicates that the call is declined due to the receiver being logged out with the specific CUID
    } else if (
      result.callEvent === CallEvent.DeclinedDueToNotificationsDisabled
    ) {
      // [Specific to Android-Platform]
      // Indicates that the call is declined due to notifications being disabled at the receiver's end
    } else if (
      result.callEvent ===
      CallEvent.DeclinedDueToMicrophonePermissionsNotGranted
    ) {
      // Indicates that the microphone permission is not granted for the call
    }
  });

//To stop observing the changes in a call state.
SignedCall.removeListener(SignedCall.SignedCallOnCallStatusChanged);

Handling Call Events in App's Killed State: Extra Steps [Android Platform]

πŸ“˜

Default Listener Behavior

For Android, the SignedCall.SignedCallOnCallStatusChanged listener receives updates only when the app is open or running in the background, not when it is in killed state.

To enable the SignedCall.SignedCallOnCallStatusChanged listener in the killed state, follow these two steps:

  1. Add the following code to the onCreate() method of your Application class:
SignedCallOnCallStatusListener.register(this);
SignedCallOnCallStatusListener.register(this)
  1. Upgrade to Signed Call React Native SDK version 0.0.5.1 and Signed Call Android SDK version 0.0.5.5.
  2. Register the SignedCall.SignedCallOnCallStatusChanged listener in the index.js file. It is the entry point of the React Native application. For a sample implementation, refer to index.js file of the Signed Call React Native Example Project.

The setup is now complete to receive call events in the killed state.

Retrieve Current Call State (Android Platform)

To retrieve the current call state, the Signed Call React Native SDK exposes the getCallState() method via SignedCall.ts class. Refer to the following code snippets to retrieve the current call state:

SignedCall.getCallState()
  .then((response: SCCallState | null) => {
    console.log('Current callState is: ' + response);
  })
  .catch((e: any) => {
    console.error(e);
  });
SignedCall.getCallState()
  .then((response) => {
    console.log('Current callState is: ' + response);
  })
  .catch((e) => {
    console.error(e);
  });

Possible call states returned by the SDK include OutgoingCall, IncomingCall, OngoingCall, and NoCall. The NoCall state is returned when there is no ongoing active call.

Return to Active Call (Android Platform)

To return to an active call, the Signed Call React Native SDK exposes the getBackToCall() method via SignedCall.ts class. Refer to the code snippets below to return to an active call:

const result = await SignedCall.getBackToCall();
if (!result) {
  console.log(
    'VoIP call is failed: ',
    'Invalid operation to get back to call'
  );
}

const result = await SignedCall.getBackToCall();
  if (!result) {
    console.log(
      'VoIP call is failed: ',
      'Invalid operation to get back to call'
    );
  }

Missed Call Solution (Android Platform)

If the receiver misses a call, the Signed Call React Native SDK shows a missed call notification to the receiver. The Signed Call React Native SDK uses action buttons on the missed call notification to display a Call to Action (CTA).

To configure the CTA on the missed call notification, add the following code:

  1. Create a Map object for missedCallActions parameter with a maximum of three entries.
let missedCallActionsMap = {
      '<Unique Identifier 1>': '<label on action-button 1>',
      '<Unique Identifier 2>': '<label on action-button 2>',
      '<Unique Identifier 3>': '<label on action-button 3>',
};
 
let initProperties = {
  ....
  missedCallActions: missedCallActionsMap,
  ....
};

SignedCall.initialize(initProperties)
  .then((response: SignedCallResponse) => {
    if (response.isSuccessful) {
      console.log('Signed Call SDK initialized: ', response);
    } else {
      console.log('Signed Call initialization failed: ', response.error);
    }
  })
  .catch((e: any) => {
    console.error(e);
  });
let missedCallActionsMap = {
      '<Unique Identifier 1>': '<label on action-button 1>',
      '<Unique Identifier 2>': '<label on action-button 2>',
      '<Unique Identifier 3>': '<label on action-button 3>',
};
 
let initProperties = {
  ....
  missedCallActions: missedCallActionsMap,
  ....
};

SignedCall.initialize(initProperties)
  .then(response => {
    if (response.isSuccessful) {
      console.log('Signed Call SDK initialized: ', response);
    } else {
      console.log('Signed Call initialization failed: ', response.error);
    }
  })
  .catch(e => {
    console.error(e);
  });
  1. Insert the following code to subscribe to the MissedCallActionClickResult event stream, allowing you to listen for CTA click events on missed call notifications:
import {
  SignedCall,
  MissedCallActionClickResult,
} from '@clevertap/clevertap-signed-call-react-native';

SignedCall.addListener(
      SignedCall.SignedCallOnMissedCallActionClicked,
      (event: MissedCallActionClickResult) => {
        console.log('SignedCallOnMissedCallActionClicked', event);
        Alert.alert(
          'Missed Call Notification!',
          event.action.actionLabel + ' is clicked'
        );
      }
    );
import {
  SignedCall,
  MissedCallActionClickResult,
} from '@clevertap/clevertap-signed-call-react-native';

//To start observing the CTA click on a missed call notification.
SignedCall.addListener(
      SignedCall.SignedCallOnMissedCallActionClicked,
      event => {
        console.log('SignedCallOnMissedCallActionClicked', event);
      }
    );

//To stop observing the CTA click on a missed call notification.
SignedCall.removeListener(SignedCall.SignedCallOnMissedCallActionClicked);

πŸ“˜

Killed state support for SignedCall.SignedCallOnMissedCallActionClicked

To enable the SignedCall.SignedCallOnMissedCallActionClicked listener in killed state, follow these two steps:

  • Upgrade to Signed Call React Native SDK version 0.0.5.1 and Signed Call Android SDK version 0.0.5.5.
  • Register the SignedCall.SignedCallOnMissedCallActionClicked listener in the index.js file, the entry point of the React Native application. For a sample implementation, refer to index.js file of the Signed Call React Native Example Project.

Debugging (All Platforms)

Signed Call React Native SDK logs are, by default, set to the LogLevel.info level. We recommend you set the log level to LogLevel.verbose mode to log warnings or other important messages during development. If you want to disable the Signed Call React Native SDK logs for the production environment, you can set the debug level to LogLevel.off.

SignedCall.setDebugLevel(LogLevel.info); //default level, shows minimal SDK integration related logging

SignedCall.setDebugLevel(LogLevel.debug); //shows debug output

SignedCall.setDebugLevel(LogLevel.verbose); //shows verbose output

SignedCall.setDebugLevel(LogLevel.off);  //disables all debugging
SignedCall.setDebugLevel(LogLevel.info); //default level, shows minimal SDK integration related logging

SignedCall.setDebugLevel(LogLevel.debug); //shows debug output

SignedCall.setDebugLevel(LogLevel.verbose); //shows verbose output

SignedCall.setDebugLevel(LogLevel.off);  //disables all debugging

The log window displays the logs from the Signed Call SDKs. After setting the debug level, search for the following tags:

PlatformTAG
Android[CT]:[SignedCall]
iOS[Signed Call]
React Native[CT]:[SignedCall]:[RN]

Sample Project of Signed Call React Native SDK Implementation:

For an example project of integrating Signed Call React Native SDK in your React Native application, refer to Signed Call React Native Example Project.

πŸ“˜

Note

Ensure you have added valid Signed Call and CleverTap Account credentials for the calling to work.

Error Handling (All Platforms)

The Signed Call React Native SDK provides error reporting and handling.

The SignedCall.initialize(..) and SignedCall.call(..) methods are associated with promise objects that receive a SignedCallResponse object. This object contains any initialization or call errors that may occur.

  • For the initialization and call errors, you may receive for the Android platform, refer to Android Error Handling.
  • For the initialization and call errors, you may receive for the iOS platform, refer to iOS Error Handling.

FAQs

Q. Is Signed Call accountId and apiKey the same as CleverTap's accountId and token?

A. No. Signed Call accountId and apiKey differ from CleverTap's accountId and token. You can find these details under your dashboard's Signed Call Settings.

Q. Why the iOS dependencies of Signed Call are not downloaded even after running the pod install?

A. As Signed Call iOS SDK uses submodules performing pod repo update, it would help download and include the required dependencies.

Q. Does the Signed Call React Native SDK support in-app calls over Bluetooth?

A. Yes. The Signed Call React Native SDK has built-in Bluetooth support. However, it requires a runtime BLUETOOTH_CONNECT permission for Android 12 and onwards.

Q. What channels are used for call routing by Signed Call React Native SDK?

A. Signed Call React Native SDK uses an active socket connection as soon as the SDK is initialized. The socket connection is a primary routing channel to receive the calls, whereas the FCM in Android platform and APNs in the iOS platform are the fallback channels in case the receiver is not connected to the socket channel. This socket connection processes the call requests raised to make a call. To know more, refer to the Best practices for initializing Signed Call SDKs.