NAV Navbar

Widget API

Introduction

The SnapCall Widget API is a JavaScript API for controlling the display and data passed to and from the SnapCall Widget. You can embed the SnapCall Widget in any website to bring calling functionalities to your visitors.

The SnapCall Widget API consists of JavaScript functions and events, which are described in the rest of this document. If you have any question or feedback feel free to email us at support@snapcall.io.

Getting started

SnapCall can be easily loaded and used to start a call with a few lines of JavaScript.

Loading SnapCall

<html>
 <head>
   <title>Getting Started With SnapCall</title>
   <script btn-bid='0846f80abb1c11e7b62e' noWidget='true' src='https://snap.snapcall.io/snapapp.min.js'>
   </script>
 </head>
 <body>
  <h3>Ready to call with SnapCall!</h3>
  <script>
    console.log('snapcallAPI is', typeof snapcallAPI !== "undefined" ? 'here!' : 'not here yet!');
  </script>
 </body>
</html>

Adding SnapCall to your website is a straightforward process. You just need to add a valid <script /> tag to your pages, for example:

<script btn-bid='0846f80abb1c11e7b62e' noWidget='true' src='https://snap.snapcall.io/snapapp.min.js'></script>

This will expose a snapcallAPI object you can use to place calls. Here is a simple HTML/JavaScript example that shows how to load SnapCall and retrieve snapcallAPI from the JavaScript console.

Load previous versions

https://snap.snapcall.io/snapapp.min.js contains the latest version of the code, if you'd like to load previous SnapCall versions, you may specify the version number in the URL value of the src attribute.

Here is an example that will load version 1.4.0: https://snap.snapcall.io/snapcalljs/snapcalljs-1.4.0-min.js

Load SnapCall asynchronously

<html>
 <head>
   <title>Getting Started With SnapCall</title>
 </head>
 <body>
   <h3>Ready to call with SnapCall!</h3>
  <script>
    console.log('snapcallAPI is', typeof snapcallAPI !== "undefined" ? 'here!' : 'not here yet!');
    window.addEventListener('snapcallEvent_init', function(e){
      console.log('[init] snapcallAPI is', typeof snapcallAPI !== "undefined" ? 'here!' : 'not here yet!');
    });
  </script>
  <script btn-bid='0846f80abb1c11e7b62e' noWidget='true' src='https://snap.snapcall.io/snapapp.min.js'>
  </script>
 </body>
</html>

If for some reason you're not able to control when SnapCall has been loaded, no worries, we'll fire a useful event named snapcallEvent_init that will help. Here is another example of retrieving snapcallAPI.

Start call

const btnBid = '0846f80abb1c11e7b62e';
const callStarted =  snapcallAPI.startCall(btnBid);
if (!callStarted){
  console.log('Something went wrong when trying to start a call')
  return;
}

window.addEventListener('snapcallEvent_callStarting', function(e){
  console.log('[callStarting] call is starting...');
});

window.addEventListener('snapcallEvent_callAnswer', function(e){
  console.log('[callAnswer] call has been answered.');
});

window.addEventListener('snapcallEvent_callEnd', function(e){
  console.log('[callEnd] call ended.');
});

Call the startCall method of the snapcallAPI object to start your call, and add various event listeners to monitor the call.

Widget functions

Checks

+ Widget is supported

snapcallAPI.widgetIsSupported(function(result) {
  /**
  * result is an object that contains the following boolean properties :
  * {
  *   hasWebRTC: boolean,
  *   hasMicrophone: boolean,
  *   hasWebcam: boolean
  * }
  */
  if (result && result.hasWebRTC && result.hasMicrophone) {
    console.log('We are good to go !');
  } else {
    console.log('We cannot start the call, sorry.');
  }
});

This function checks if both the browser and the device could work with the SnapCall service. You must provide a callback function whose argument is an object passed by SnapCall that contains the requested response.

const res = snapcallAPI.widgetIsSupported(callbackFunction);

argument optional type
callbackFunction no function

+ Media server accessible

snapcallAPI.isMediaServerAccessible(function(result) {
  /**
  * result is boolean that indicates success or failure
  */
  if (result) {
    console.log('Media server accessible');
  } else {
    console.log('Failed to access media server');
  }
});

Checks if we're able to contact the media server we'll send/receive voice traffic to/from. If placing calls from restricted access networks, you may want to use this function to check that voice traffic won't get blocked.

snapcallAPI.isMediaServerAccessible(callbackFunction);

This function is also available as a Promise:

snapcallAPI.isMediaServerAccessible().then((res) => { console.log('Media server check result :', res) });

argument optional type
callbackFunction yes function

+ Loopback test

snapcallAPI.loopbackTest(function(result) {
  console.log('result :', result);
})
// produces 'result : {error: false, lossPercent: 0, avgRTT: 18}'

Performs a test against SnapCall's media servers. We send two seconds of simulated audio traffic and report back with the following indicators:

snapcallAPI.loopbackTest(iceServers, callbackFunction);

iceServers can be replaced with another array of TURN server configuration, defaults to SnapCall's TURN server.

This function is also available as a Promise:

snapcallAPI.loopbackTest().then((res) => { console.log('Loopback test result :', res); })

argument optional type
iceServers yes array
callbackFunction yes function

+ Widget is active

const result = snapcallAPI.widgetIsActive(function(res) {
  if (res) {
    console.log('Widget is active');
  } else {
    console.log('Widget is not active');
  }
});

if (!result) {
  console.log('snapcallAPI.widgetIsActive called with bad arguments?');
}

This function checks whether the widget is active. A callback function to retrieve the result of the operation is needed, the function itself will return false if it was called with invalid arguments. bid is optional if it has been provided as an <script /> attribute (btn-bid), or if snapcallAPI.initWidget() has been called previously.

const result = snapcallAPI.widgetIsActive(bid, callbackFunction);

argument optional type
bid yes string
callbackFunction no function

+ Widget is open

const result = snapcallAPI.widgetIsOpen(function(res) {
  if (res) {
    console.log('Widget is open');
  } else {
    console.log('Widget is closed');
  }
});

if (!result) {
  console.log('snapcallAPI.widgetIsOpen called with bad arguments?');
}

This function check whether the widget is open according to its assigned opening hours. A callback function to retrieve the result of the operation is needed, the function itself will return false if it was called with invalid arguments. bid is optional if it has been provided as an <script /> attribute (btn-bid), or snapcallAPI.initWidget() has been called previously.

const result = snapcallAPI.widgetIsOpen(bid, callbackFunction);

argument optional type
bid yes string
callbackFunction no function

+ Widget is available for a call

const result = snapcallAPI.widgetIsAvailable(function(res) {
  if (res) {
    console.log('Widget is available');
  } else {
    console.log('Widget is closed or inactive');
  }
});

if (!result) {
  console.log('snapcallAPI.widgetIsAvailable called with bad arguments?');
}

This function check if the widget is active and open. A callback function to retrieve the result of the operation is needed, the function itself will return false if it was called with invalid arguments. bid is optional if it has been provided as an <script /> attribute (btn-bid), or snapcallAPI.initWidget() has been called previously.

const result = snapcallAPI.widgetIsAvailable(bid, callbackFunction);

argument optional type
bid yes string
callbackFunction no function

Operations

+ Init the widget

Simple widget initialization

const result = snapcallAPI.initWidget('0846f80abb1c11e7b62e0611b42a59b4');

Initialize widget in API mode, pass a callback function that checks if widget is valid

const result = snapcallAPI.initWidget('0846f80abb1c11e7b62e0611b42a59b4', {
    noWidget: true
  },
  function(res) {
    if (res) {
      console.log('Your widget is valid, thanks!');
    } else {
      console.log('Invalid widget');
    }
  });

This function loads the widget specified with its bid. It will return false if bid is empty or is not a string, true otherwise.

Please note that the returned value from this function does not reflect the validity of the widget identified with the bid argument. Use a callback function instead and check the argument passed to it.

const result = snapcallAPI.initWidget(bid, options, callbackFunction);

argument optional type
bid no string
options yes object
callbackFunction yes function

+ Switch widget target (requires a visible widget)

const result = snapcallAPI.newWidgetTarget('0846f80abb1c11e7b62e0611b42a59b4');

This function loads a widget identified with bid and applies the current configuration.

argument optional type
bid no string

+ Hide the widget

Hides the button loaded to the DOM.

snapcallAPI.hideWidget();

This function removes the widget from the DOM.

+ Show the widget

Shows the button loaded to the DOM.

snapcallAPI.showWidget();

This function inserts the HTML elements of the widget in the DOM. If the widget is not present in the DOM, then the HTML will be created again and added to the DOM. If the widget is present in the DOM, the HTML will be reloaded.

Actions

+ Start call

Will return false if button identifier has not been set earlier either as a <script /> attribute or from snapcallAPI.initWidget()

const result = snapcallAPI.startCall(function(res) {
  console.log('res :', res);
});

console.log('result :', result);

This function start a call and return true or false.

const result = snapcallAPI.startCall(bid, callbackFunction);

argument optional type
bid yes string
callbackFunction yes function

+ Stop call

snapcallAPI.endCall();

This function stops a call.

snapcallAPI.endCall();

+ Rate call

snapcallAPI.rateCall(4, function(error, result){
  if (!error && result == true) {
    console.log("Call rate is a success");
  }
})

This function allows to set a rate to the call between 0 and 5. May return false in case of malformed arguments, optional callback function can be used too.

const result = snapcallAPI.rateCall(number, callbackFunction);

argument optional type
number no number (between 0 and 5)
callbackFunction yes function

+ Send info

snapcallAPI.sendInfo({data: 'value', ['one', 'two', 'three']}, function(res){
  console.log("sendInfo result :", res);
})

During a call, you may want to send arbitrary data between endpoints, like e.g. text chats. This function will transform the JavaScript object passed as the first argument to JSON, and transfer it to the remote endpoint. From there, the remote endpoint code must listen to the snapcallEvent_receiveInfo event to receive and process the data.

An optional callback function can be used to make sure the message has been successfully delivered.

snapcallAPI.sendInfo(obj, callbackFunction);

argument optional type
data no object (must be convertible to JSON)
callbackFunction yes function

+ Context

window.snapcallExtraContext = function(callback){
  callback({
    id: 123456,
    fn: 'john',
    ln: 'doe',
    e: 'john@doe.com',
    ...
  });
};

Sometimes you need to push some datas from your webapp throw the call. Function below has to be set on your webapp js code, when it is set all the JSON data will be push in the SnapCall call when it start.

window.snapcallExtraContext(callback)

To fetch these datas in the other side, you could use our REST API.

Events

SnapCall widget API provides event notifications for all the call process. Whenever an event triggers, this notification can enable custom JavaScript to use that trigger to provide additional functionality.

You can listen to the following events:

window.addEventListener('snapcallEvent_init', function(e){
    if (e.detail.data.success == true)
        console.log('script loaded');
});
events description
snapcallEvent_init SnapCall script is ready to use
snapcallEvent_bidLoad Widget with bid selected is loaded and ready to use
snapcallEvent_mediaRequest Microphone access requested
snapcallEvent_callStarting Call is connected
snapcallEvent_callAnswer Call is answered
snapcallEvent_callCurrentTimer Call timer (updated each sec)
snapcallEvent_callEnd Call is finished
snapcallEvent_callOnHold remote has changed the hold status
snapcallEvent_callDisconnect Call is disconnected (network issue)
snapcallEvent_callReconnect Call is reconnected
snapcallEvent_audioLevelLocalMicrophone Get audio level from local microphone
snapcallEvent_receiveInfo Get information data from remote endpoint

Some event has a detail property, which can include the following available information about the decision:

detail property JSON object

  data:
    description: 'call is currently starting'
    success: true
detail.data.success description
true event is ok you can add some action
false event went wrong for some reasons
pending event is waiting for an answer
  data:
    description: 'call is currently starting'
    success: true
    time: 3

snapcallEvent_callCurrentTimer has a detail property named time which give the value of the timer in sec.

  data:
    success: true
    value: 32

snapcallEvent_audioLevelLocalMicrophone has a detail property named value which give the value of the local audio microphone level. 0 value during some sec can assume that local microphone is muted.

snapcallEvent_callOnHold has a detail property value with the value of true if the remote put the call on hold and false if unhold

  data:
    value: true

snapcallEvent_mediaRequest has a detail with the properties 'success' set to true, false or pending (processing) and deltaTime time in ms between the request and the validation

  data:
    success: true
    deltaTime: 1800

Android SDK

v2.4.0

The SnapCall native libraries for android allow you to embed our call features easily in your apps.

If you have any questions or feedback please feel free to write an email at support@snapcall.io or open an issue

Requirements

Building

Created with Android Studio 3.5.1

minSdkVersion is set to 16 - and target/compileSdkVersion is set to 28

For any problems with the build or compilation please contact Snapcall.

Hardware

Works on Phones and Tablets with Android superior to 4.1 (Jelly_Bean)

Tested with Android 9 (Pie).

Install

Gradle

Compiled and tested with gradle version 3.5.1 and google service version 4.3.2

add java 8


  android {
  ...
  compileOptions {
        sourceCompatibility = '1.8'
        targetCompatibility = '1.8'
  }
  ...
  }

Compile with java 8 support : To add java 8 support add to your app.gradle :

Linked library

** annotation **

implementation 'com.android.support:appcompat-v7:28.+'

Snapcall requires the okhttp3 library which must be added to your project - if you use another version of this library and an error occurs , please contact Snapcall.

Compile

[!!!] This part has been updated for latest gradle version. Theire are a path issue from gradle 3.5.1 that is fixed here.

Add these lines to your App build.gradle file, it's a graddle function that download a github release.

def getGitHubRelease = { name, version, githubRepo = "snapcall/snapcall_sdk_android" ->
    String aarVersion = version
    String aarName = name
    String url = "https://github.com/${githubRepo}/releases/download/"
    File file = new File("$projectDir/libs/${name}-${version}.aar")
    if (!file.exists()) {
        delete fileTree("$projectDir/libs/").matching { include "${aarName}*.aar" }
        file.parentFile.mkdirs()
        try {
            new URL("${url}${aarVersion}/${aarName}.aar").withInputStream { downloadStream ->
                file.withOutputStream { fileOut ->
                    fileOut << downloadStream
                }
            }
        } catch (Exception e) {
            throw new GradleException("getGithubRelease: could not download aar file")
        }
    }
    if (!file.exists())
        throw new GradleException("getGithubRelease: could not find aar file")
    files(file.absolutePath)
}

The android SDK file is an AAR. It needs to be linked to your app. You can download our release here or on the github repository.

For manual installation add com.squareup.okhttp3:okhttp:3.12.1 dependency

On the right, find the steps to integrate the sdk into your app directly with graddle.

Add this to your dependencies in your App gradle file

dependencies {
  implementation getGitHubRelease('snapcall_android_framework', '2.4.1')
  implementation 'com.squareup.okhttp3:okhttp:3.12.0'
  implementation 'org.webrtc:google-webrtc:1.0.28513'
}

Now you can synchronise, Snapcall is ready to use!

Get started

Get the snapcall Object

  Snapcall sc = Snapcall.getInstance();

The first object you use in order to make a call is an instance of the Snapcall class. It's a singleton so you access it with a static getter that will instantiate it if needed :

public static Snapcall getInstance()

Request the microphone access

public class myActivity extends Activity {

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       Snapcall.getInstance().askForPermissions(this);
  }
}

From Android 23 microphone access has to be directly asked to the user in addition to be added to the Manifest. We provide a function that check if the permissions is not granted and make the request if necessary.

public final void askForPermissions(Activity ctx)

parameters description
Context ctx Application Context or an Activity Context

check the microphone access

public class myActivity extends Activity {

  private void checkMicrophoneStatus() {
    boolean microphonePermissionGranted = Snapcall.getInstance().permissionStatus(this);
  }
}

Later you can get the status of the permissions :

public final boolean permissionStatus(Context ctx)

parameters description
Context ctx Application Context or an Activity Context

You can go further by studying the android documentation :

launch a call

  Button myButton = new Button(context);  //|| = (Button)findViewById(R.id.mybutton);

  myButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              try {
                String TAG = "SnapCall";
                Context context = this;
                Snapcall.getInstance(getApplicationContext(), ,"88b3d0f3a44311e78f9b0ae03a1ae33f", null);Snapcall.getInstance().launchCall(context, bidId, null);
              }catch (SnapcallException e){
                Log.e(TAG, e.cause);
              }
            }
          });
  myLayout.addView(myButton);

You can now try to launch a call :

final public void launchCall(@NonNull Context context, @NonNull String bidId, @Nullable Snapcall_External_Parameter parameter) throws SnapcallException

parameters description
Context ctx Application Context or an Activity Context
String bidId the identifier you create on our dashboard
Snapcall_External_Parameter parameter parameters for a call
throws description
SnapcallException throw if the microphone access fail

Snapcall parameter

  Snapcall_External_Parameter param = new Snapcall_External_Parameter();

  param.callTitle = "Snapcall";
  param.displayName = "John";
  param.displayBrand = "Doe";
  param.textColor = #FF0000;
  param.backgroundColor = #00FF00;
  param.externalContext = new JSONObject(); // {id: 123456, fn: 'john',ln: 'doe',e: 'john@doe.com',...}
  param.AssetPathFont = "fonts/snapcallfont.otf";
  param.AssetPathImage = "images/snapcall_icon.png";
  // or
  param.urlImage = "https://mydomaine.ext/awesomeImage.png";
  // or
  param.setResImage("snapcall_icon", getPackageName());

When the call start you can see our default user interface. You can configure this interface with this class.

You can also replace this default interface.

assets is a folder named assets in your project while res is the folder containing drawable, layout, etc...

properties type description
callTitle String The title that will be displayed on the top of the UI
externalContext JSONObject Json Data you can link to the call
displayName String The name that will be displayed in the UI
displayBrand String Brand/family name that will be displayed in the UI
textColor int The text color that will be set in the snapcall call UI
backgroundColor int The background color that will be set in the snapcall call UI
AssetPathFont String asset font to display in the call interface
AssetPathImage String asset image to display in the call interface
urlImage String URL of an image that will be displayed in the UI0
showBackButton boolean when you want to display a button that leave the UI on click.

for an image in the res folder call the function setResImage :

public void setResImage(String name, String packageName)

parameters description
String name name of the image without extension
String package you package as a string

Receive a call (2.4.0 and later)

We give you the ability to call back your user directly in your app. This work with push notification (we never keep an open connection) and Firebase. We are directly connected to the Firebase server via the XMPP API and are able to control and distribute push to make call.

Firebase

Firebase is the push provider for Android phone powered by Google. We use the cloud messaging in order to contact your user. Find everything to configure firebase in your project here. You will need to send to snapcall a server token to allow us to send the payload that will start the call. Create a new one in the Firebase console and add it to the snapcall dashboard.

User Token

public class FirbaseToken implements OnCompleteListener<InstanceIdResult> {
    @Override
    public void onComplete(@NonNull Task<InstanceIdResult> task) {
        if (!task.isSuccessful()) {
            Log.w("onComplete", "getInstanceId failed", task.getException());
            return;
        }
        InstanceIdResult res = task.getResult();
        if (res != null) {
            String id = res.getId();
            String token = res.getToken();
            Snapcall.registerTokenForCallback("snapcall_test", token);
            Log.d("onToken", String.format("id : %s,  token : %s", id, token));
            return;
        }
    }
}

public class App extends Application {

  private function initFirebase() {
    FirebaseInstanceId.getInstance().getInstanceId()
                .addOnCompleteListener(new FirebaseToken());
  }
}

The code above show how to get your firebase Token and register it in Snapcall, It will be added to the context of the next call made by your user. You will be able to call back from your zendesk application.

Receive message - Manifest change

  <service
    <!-- name of the class -->
    <android:name=".PushService">
    <intent-filter>
      <action android:name="com.google.firebase.MESSAGING_EVENT"/>
    </intent-filter>
  </service>

Register Firebase in your manifest. PushService in the code above will be the class extending the Firebase class that will be used to communicate with the firebase SDK.

Receive message - get Snapcall data

  public class  PushService extends FirebaseMessagingService {
    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
      Map data = remoteMessage.getData();
      String origin = (String) data.get("origin");
      final Snapcall_External_Parameter callParam =  new Snapcall_External_Parameter();
      if ("snapcall".equals(origin)) {
        try {
          Snapcall.getInstance().receivePushCall(this, data, callParam);
        } catch(Exception e) {}
      }
    }
  }

The code above quickly show how to extend firebase class to receive the Snapcall data. It will get the push payload in the RemoteMessage and start the call.

Device specific and push delivery

Notification system depend of the system implementation of android. Android system is open source and any constructor is able to modify the OS for is need. On some device push delivery can be restricted. We identify many case described here , and the behavior is even not programatic but depend of the setting of the phone. Here we assume that your device is connected to a network wifi or 4g that allow it to connect to Google and Snapcall server.

As we use background to get the push notification, disabled notification doesn't affect us on Android. It could just create a bad user experience as the user will not be able to relaunch the call UI from anywhere. You should always add a button to return to call UI from your app.

How it works when app is killed (Force Stopped)

On a normal android os (not Huawei's phone) , even if the app is swiped , there is still a process that work, allowing us to get the background notification needed to start the call. But Android application can be killed (force stopped). There is many way to kill an app, call process.exit from code (any android developer know he should avoid this), stop from the app setting or kill it from a third party monitoring app (and on some Huawei's and Xiaomi phone swipe an app from the history cause the same behavior). But you can push your user to enable option that improve push notification reception with the correct explanation. In that webpage, developers from oneSignal, a company that provide server and client implementation for push message delivery, written explanation of how to handle such case by phone brand and manufacturers.

Find on this link on stack overflow how to launch the setting windows for a Huawei in java. In the answer they show how to implement a setting request to your user. This is necessary on Huawei, otherwise the user will not be able to receive call as soon as the screen is turned off.

more documentation :

this was written thanks to the nice documentation from one signal here , find more about device reliability in this page

Low Power, Energy Saving, Do Not Disturb Mode

If the battery of the phone is low push can be blocked to gain battery time. This can be unactivated from Settings > Battery > Battery Optimization or Adaptive Battery > Find your app and press Don't optimize. This action has to be done by your user.

Listen call event

Create the communication object

  public class SnapcallListener {
    SCClient client;
    Context context;

    snapListener(@NonNull Context context) {

      this.context = context;
      client = new SCClient(context);
      client.connect()
    }

    public void onDestroy() {
      context = null;
      client.disconnect();
      client.release();
    }
 }

You can get get the different event coming from the call in your app. For that we provide the SCClient class that will connect to the Service hosting the call. The connect function start listening snapcall event. The disconnect function stop listening snapcall event. The release function release all the reference on the context. After this function the object could not be used anymore

public SCClient(Context context)

parameters description
Context ctx Application Context or an Activity Context

Event


public class SnapcallListener {
  SCClient client;
  Context context;

  snapListener(@NonNull Context context) {

    this.context = context;
    client = new SCClient(context);
    client.setListener(this);
    client.connect()
  }

  @Override
  public void onConnectionReady(SCClientEvent parameter) {}

  @Override
  public void onCreated(SCClientEvent parameter) {}

  @Override
  public void onRinging(SCClientEvent parameter) {}

  @Override
  public void onAnswer(SCClientEvent parameter) {}

  @Override
  public void onInternetDown(SCClientEvent parameter) {}

  @Override
  public void onInternetUP(SCClientEvent parameter) {}

  @Override
  public void onHangup(SCClientEvent parameter) {}

  @Override
  public void onHeld(SCClientEvent parameter) {}

  @Override
  public void onUnheld(SCClientEvent parameter) {}

  @Override
   public void onUnhook(SCClientEvent parameter) {}

  @Override
  public void onConnectionShutDown() {}

  @Override
  public void onMessage(String callID, String message) {}

  @Override
  public void onMessage(String callID, Integer message) {}

  @Override
  public void onMessage(String callID, JSONObject message) {}

  @Override
  public void onTime(SCClientEvent parameter) {}

  @Override
  public void onError(CallError error) {
    new AlertDialog.Builder(context)
      .setTitle("Snapcall Error")
      .setMessage(error.getDescription())
      .setPositiveButton("agreed", null)
      .setIcon(android.R.drawable.ic_dialog_alert)
      .show();
  }

  public void onDestroy() {
    context = null;
    client.disconnect();
    client.release();
  }
}

Event Name description
onConnectionReady connected to the service, you can start to use the SCClient api.
onCreated The Call has been successfully created and is about to start.
onRinging sound start , user entering the queue and wait for answer.
onAnswer call has been answered. When calling Agent you can get the agent mail here. called again if an agent transfer occurs.
onInternetDown a network disconnection has been detected, we will try to reconnect.
onInternetUP network back, we reconnect the call.
onHeld agent put the call on hold.
onUnheld agent removed the hold.
onUnhook user answer the call.
onTime called every second , the time value will be updated.
onHangup the client or the agent hangup, call is going to end.
onError called if we can't connect the media.
onConnectionShutDown no more call the service has shutdown.
onMessage You receive a message from your agent.

In each event you receive a SCClientEvent that represents the phone and call state.

Event parameter

hardware state - SCClientEvent


public void processSnapCallEvent(SCClientEvent parameter){

  boolean connectivityStatus = parameter.isConnected(); // the internet connection is working
  boolean mute = parameter.isMute(); // is the microphone muted
  boolean speaker = parameter.isSpeaker(); // is the sound output on loud speaker or on Headphone/normal speaker
  SCCall call = parameter.getCall(); // getter for the Call Object
}

public class SCClientEvent implements Parcelable

The SCClientEvent allow you to get the network status , the microphone status and the audio output.

public boolean isMute() : return true if the cal is mute.

public boolean isSpeaker() : return true if the speaker is active false if you use headphone or call speaker.

public boolean isConnected() : return the current Network status.

public SCCall getCall() : return the call object, can be null.

Call state - SCCall


  SCCall call = parameter.getCall();
  if (call != null) // call is created
  {
    String id = call.getCallIdentifier(); // an unique identifier for your call , hex string.
    String agent = call.getAgentMail(); // if answered in a Zendesk account we provide the mail of the agent.
    String state = call.getCurrentCallState(); // state of call : STATE_CREATED, STATE_CONNECTED , STATE_RECONNECT, STATE_TERMINATED
    long startedDate = call.getDate(); // the date at
    Date realDate = new Date(startedDate);
    long duration = call.getDuration(); // time in seconde the call was live
    long timer = call.getStartedTime(); // Time from Boot to make a time counter
    Chronometer chrono = new Chronometer(Context); // you can set this long directly in an Android chronometer and start it
    chrono.setBase(time);
    chrono.start();
    String displayBrand = call.getDisplayBrand(); // the Brand configured for the button in the dashboard
    String displayName = call.getDisplayName(); // the Name configured for the button in the dashboard
    boolean heldStatus =call.isHeld(); // if the agent put the call on held.
    boolean isTransferred = call.isTransferred(); // if the agent transferred the call to an another agent.
    boolean isRinging = call.isRinging(); // if we are in a ring state
  }

public String getCallIdentifier() : return an unique identifier for your call.

public String getCurrentCallState() : return the state of the call. the state can be one of the four defined in SCClient :

STATE description
public final static String STATE_CREATED = "STATE_CREATED"; the call has been created
public final static String STATE_CONNECTED = "STATE_CONNECTED"; the call is running
public final static String STATE_RECONNECT = "STATE_RECONNECT"; an error has occurred we try to reconnect
public final static String STATE_TERMINATED = "STATE_TERMINATED"; the call hangup

public String getDisplayBrand() the Name you submit for display in the dashboard or in the snapcall parameters

public String getDisplayName() the brand you submit for display in the dashboard or in the snapcall parameters

public long getStartedTime() return the second from boot at call start

public long getDuration() return the number of second since call start

public Long getDate() return the date as a long

public boolean isHeld() return true if call is on hold.

public String getAgentMail() return the agent mail.

public boolean isTransferred() return true if the call has been transferred to a new agent.


public void putCallInDB(SCCall call){

  this.db.store(SCCall.toBytesArray(call));
}

public void getCallFromDB() {

  SCCall call = SCCall.fromBytesArray(this.db.get());
}

The SCCall implement the Parcelable and Serializable inteface, thats mean you can transform it to a byte array and store it into a file or a database , and get it back. It will allow you , by example to make a call history or retrieve the last agent that answered a call for this user.

public static byte[] toBytesArray(@NonNull SCCall call) throws IOException

argument description
SCCall call the SCCall you received from hangup event.
throws return
IOException byte[]

public static SCCall fromBytesArray(byte[] bytes) throws ClassNotFoundException, IOException

argument description
SCCall call the SCCall you received from hangup event.
throws return
IOException, ClassNotFoundException SCCall

Make Your own User Interface

start the View

Snapcall.getInstance().setUseDefaultInterface(false);

// for an activity onNewIntent will be called
Intent launchCallUI = new Intent(getApplicationContext(), MainActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_SINGLE_TOP);
Snapcall.getInstance().setLoadUIIntent(launchCallUI);
Snapcall.getInstance().setStartActivityOnReady(isAnActvity?);

// for a broadcastListener onReceive will be called

Intent launchCallUI =  new Intent();
launchCallUI.setAction(interfaceRequestedAction);
Snapcall.getInstance().setLoadUIIntent(launchCallUI);

All this Event allow you to make your own user interface. This code let you configure snapcall in order to display your own interface, on start or on notification click.

When the Service judge that the UI can be launched an intent is sent or an activity is created. This will also be used if the user leave your app , on notification click this Intent will be sent again. If you want to start an activity with this intent you can set StartActivityOnReady to true, and startActivity will be called . If not you can use a broadcastListener. documentation broadcastReceiver documentation Activity

Calling view

public function handleSnapcallEvent(SCCall call) {
  if (parameter == null) {
    setContentView(snapcallView.Loading);
    return ;
  }
  setContentView(snapcallView.Calling);
  hangupButton.setOnClickListener((View button)->{
    snapcallClient.hangup();
  });
}

Ringing View

public function handleSnapcallEvent(SCCall call) {
  if (parameter == null) {
    setContentView(snapcallView.Loading);
    return ;
  }

  boolean isRinging = call.isRinging();
  if (isRinging) {
    setContentView(snapcallView.Ringing);
    answerButton.setOnClickListener((View button)->{
      snapcallClient.answer();
    });
  } else {
    setContentView(snapcallView.Calling);
  }

}

The isRinging function in the call allow you to know which type of interface need to be setup. You will need to add an answer button to unHook the call.

SCClient API

hangup

    try {
       client.hangup(); // hangup the call
     } catch (SCClientException e){
       Log.e("snapcall", "client not connected"); // The Client is not reachable
       finish();
     }

public void hangup() throws SCClientException

argument description
throws description
SCClientException throw if the client is not connected or the service not launched.
return description
void

mute

    try {
       boolean isMute = client.mute();
     } catch (SCClientException e){
       Log.e("snapcall", "client not connected"); // The Client is not reachable
     }

public boolean mute() throws SCClientException

argument description
throws description
SCClientException throw if the client is not connected or the service not launched.
return description
boolean if the call is currently muted

setSpeaker

    try {
       boolean speakerIsActivated = client.setSpeaker();
     } catch (SCClientException e){
       Log.e("snapcall", "client not connected"); // The Client is not reachable
     }

public boolean setSpeaker() throws SCClientException

argument description
throws description
SCClientException throw if the client is not connected or the service not launched.
return description
boolean if the speaker is activated

answer

    try {
       client.answer();
     } catch (SCClientException e){
       Log.e("snapcall", "client not connected"); // The Client is not reachable
     }

public boolean answer() throws SCClientException

argument description
throws description
SCClientException throw if the client is not connected or the service not launched.
return description
boolean true if call running

unlock Screen

public class ActivitySnapCall extends Activity {

  public void onCallRing() {
       client.unlockScreen(this);
  }
}

public void unlockScreen(Activity context)

This function allow to put your call activity on the screen even if it is locked and turn off.

argument description
Activity the activity to put on the screen.
throws description
return description

start ring

public class ActivitySnapCall extends Activity {

  public void onCallRing() {
       SCClient.startRing(this, true, null);
  }
}

static public void startRing(Context context, boolean vibrate, @Nullable long[] pattern)

This notify the user of the call with the default ringtone and vibration.

argument description
Context context for the app
boolean vibrate if we launch the vibration, Manifest permission required
long[] pattern pattern for the vibration , null for default
throws description
return description

sendInfo

    try {
      JSONObject info = new JSONObject();
      info.accumulate("clientID", myClientID);
      client.sendInfo();
     } catch (SCClientException e){
       Log.e("snapcall", "client not connected or message failled to be send");
     }

Send a message to the client of your Agent.

public void sendInfo(JSONObject infoMessage) throws SCClientException

argument description
JSONObject infoMessage the json representation of your message
throws description
SCClientException ("connection error") throw if the client is not connected or the service not launched.
SCClientException ("failed to send data to the server") throw if we failed to send the data to your agent.
return description
void

isClientConnected


       boolean SnapcallIsRunning = client.isClientConnected();

check if the snapcall Service is running

public boolean isClientConnected()

argument description
JSONObject infoMessage the json representation of your message
throws description
return description
boolean true if the service is running

getCurrentState

  try {
    SCClient currentState = client.getCurrentState();
   } catch (SCClientException e){
     Log.e("snapcall", "client not connected");
   }

return the current SCClient Event.

public SCClientEvent getCurrentState() throws SCClientException

argument description
throws description
SCClientException throw if the client is not connected or the service not launched.
return description
SCClientEvent the current state

setListener

    client.setListener(mySnapcallListener);

set the listener for a SCClient instance.

public void setListener(SCClientListener listener)

argument description
SCClientListener listener a SCClientListener interface or null to remove it.
throws description
return description

Snapcall API

getInstance

      Snapcall.getInstance();

static method that return the current Snapcall Instance.

public static Snapcall getInstance()

argument description
throws description
return description
Snapcall a Snapcall instance.

releaseInstance

      Snapcall.releaseInstance();

remove the reference held by snacpcall

public static void releaseInstance()

argument description
throws description
return description

askForPermissions

      Snapcall.getInstance()l.askForPermissions(myActivity);

Check for microphone permissions and request it if needed.

public final void askForPermissions(Activity ctx)

argument description
Activity ctx your activity in order to show the microphone request
throws description
return description

permissionStatus

      boolean permissionGranted = Snapcall.getInstance().permissionStatus(Context ctx);

check if the permission for microphone has been granted.

public final boolean permissionStatus(Context ctx)

argument description
Context ctx context for your app
throws description
return description

buttonIsClosed

      Snapcall.getInstance().buttonIsClosed(bidId, new Snapcall.activate() {
                @Override
                public void callback(boolean Success) {
                  if (success) {
                    // call can be made.
                  }
                }
              });

      // java 8
      Snapcall.getInstance().buttonIsClosed(Bid, (res) -> {
              if (res != result) {
                // call can be made.
              }
              });

Check if the call can be made , a call can not be made if api is not rechable, if you inactive your button or we are out of the schedule.

final public void buttonIsClosed(@NonNull String Bid_id, @NonNull final activate cb)

argument description
String Bid_id the bid that you will use for a call.
final activate cb the callback interface for the request.
throws description
return description

launchCall

Button myButton = new Button(context);  //|| = (Button)findViewById(R.id.mybutton);
myButton.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
            try {
              String TAG = "SnapCall";
              Context context = this;
              Snapcall.getInstance().launchCall(getApplicationContext(), ,"88b3d0f3a44311e78f9b0ae03a1ae33f", null);
            }catch (SnapcallException e){
              Log.e(TAG, e.cause);
            }
          }
        });
myLayout.addView(myButton);

launch a call.

final public void launchCall(@NonNull Context context, @NonNull String bidId, @Nullable Snapcall_External_Parameter parameter) throws SnapcallException

argument description
Context context context for your app
String bidId the identifier you create on our dashboard
Snapcall_External_Parameter parameter parameters for a call
throws description
SnapcallException if we can't access the microphone
return description
void

Receive call

  public class  PushService extends FirebaseMessagingService {
    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
      Map data = remoteMessage.getData();
      String origin = (String) data.get("origin");
      final Snapcall_External_Parameter callParam =  new Snapcall_External_Parameter();
      if ("snapcall".equals(origin)) {
        try {
          Snapcall.getInstance().receivePushCall(this, data, callParam);
        } catch(Exception e) {}
      }
    }
  }

launch a call after you received a push.

final public void receivePushCall(@NonNull Context context, Map message , @Nullable Snapcall_External_Parameter parameter) throws SnapcallException{

argument description
Context context context for your app
Map message mdata received from firebase
Snapcall_External_Parameter parameter parameters for a call
throws description
SnapcallException if we can't access the microphone or data are missing
return description
void

restorCallUi

  Button myButton = new Button(context);  //|| = (Button)findViewById(R.id.mybutton);
  myButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              try {
                Snapcall.getInstance().restorCallUi(getApplicationContext());
              }catch (SnapcallException e){
                Log.e(TAG, e.cause);
              }
            }
          });
  myLayout.addView(myButton);

Put the call user interface to the front.

final public void restorCallUi(Context ctx)

argument description
Context ctx context for your app
throws description
return description

getLastCall

    SCCall lastCall = Snapcall.getLastCall(myActivity);
    String agentMail = lastCall.getAgentMail();

get the last call made from history and return it. You can use this function at startup in order to rate the last call made if not done.

public SCCall getLastCall(Context context)

argument description
Context ctx context for your app
throws description
return description
SCCall a SCCall representing the call or null if not found

rateCall

      SCCall lastCall = Snapcall.getInstance().getLastCall(myActivity);
      if (lastCall != null){
          Log.e("lastCall", lastCall.getAgentMail());
          Snapcall.getInstance().rateCall(lastCall, 3, new Snapcall.RequestResult() {
                    @Override
                    public void onSuccess(@Nullable String result) {
                    }
                    @Override
                    public void onError(String error, @Nullable Throwable e) {
                    }
                });
      }

Make a request in order to add a rate to the call.

public boolean rateCall(SCCall call, int rate, @Nullable final RequestResult callback)

argument description
SCCall call a SCCall object representing the call you want to rate.
int rate an int between 1 and 5.
RequestResult callback a callback in order to get the result of the request.
throws description
return description
void

rateLastCall

    SCCall lastCall = Snapcall.getInstance().getLastCall(myActivity);
    if (lastCall != null){
        Log.e("lastCall", lastCall.getAgentMail());
        Snapcall.getInstance().rateLastCall(3, new Snapcall.RequestResult() {
                  @Override
                  public void onSuccess(@Nullable String result) {
                  }
                  @Override
                  public void onError(String error, @Nullable Throwable e) {
                  }
              });
    }

Add a rate to the last call.

public boolean rateCall(SCCall call, int rate, @Nullable final RequestResult callback)

argument description
int rate an int between 1 and 5.
RequestResult callback a callback in order to get the result of the request.
throws description
return description
void

deviceHasMicrophone

    boolean isMicrophoneAccessible = Snapcall.getInstance().deviceHasMicrophone(myActivity);

Check if the devise have a microphone.

public boolean deviceHasMicrophone(Context context)

argument description
Activity ctx your activity
throws description
return description
boolean true if the device have a microphone

setLoadUIIntent

  // for an activity
  Intent restor = new Intent(getApplicationContext(), myActivity.class).addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_HISTORY);
  Snapcall.getInstance().setLoadUIIntent(restor);

  // for a broadcast listener
  Intent restor =  new Intent();
  restor.setAction("interfaceRequestedAction");
  restor.putExtra("staticApplicationKey", intentKey);
  Snapcall.getInstance().setLoadUIIntent(restor);

When building your own user interface set this intent in order to receive the ui request. For example if your app is removed from the history by the user, the call will continue. If the user click on the notification added by snapcall during the call this intent will be sent and you will be able to re load your app.

public void setLoadUIIntent(Intent intent)

argument description
Intent intent an intent object that will be send with startActivity or sendOrderred Broadcast
throws description
return description

setUseDefaultInterface


Snapcall.getInstance().setUseDefaultInterface(false);

Call this function if you doesn't want to use the default UI provided by snapcall.

public void setUseDefaultInterface(boolean useNativeInterface)

argument description
boolean useNativeInterface false if you made your own call user interface. default is True.
throws description
return description

setStartActivityOnReady

Snapcall.getInstance().setStartActivityOnReady(true);

Call this function in addition of setUseDefaultInterface if the intent used to launch snapcall need to be sent with startActivity rather than sendOrderredBroadcast. In this case the flag FLAG_ACTIVITY_NEW_TASK will always be added to your intent.

public void setStartActivityOnReady(boolean startActivityOnReady)

argument description
boolean startActivityOnReady true if the receiver for your intent is an activity.
throws description
return description

iOS SDK

The SnapCall native libraries for iOS allow you to embed our call features easily in your apps. If you have any questions or feedback please feel free to write an email at support@snapcall.io or open an issue

Requirements

Building

Created with XCode Version 10

Base Sdk set to latest iOS (iOS 12.x)

Doesn't support Bitcode.

For any problems with the target or compilation please contact Snapcall.

Hardware

Works on Phones and Tablets with iOS superior to 10.

Install

Pod

To get Snapcall use our Cocoapod :

pod 'Snapcall_Framework' , '5.5.2'

Carthage [→]

Add this line to your Cartfile.

github "snapcall/snapcall_sdk_ios" == 5.5.2

Linked library

If there are any problems with version compability please contact us.

Plist file

You must add privacy information to your plist (camera privacy is requested even if you don't use it) :

Get started

Callkit

In iOS Apple provid a library in order to add VOIP features for an app. It have his own user interface when screen is locked and allow to process the call like a normal call. For example it will automatically add the call to the call history.

Get the Snapcall Object

  let sc = SnapCall.getInstance()

The first object you use in order to make a call is an instance of the Snapcall class. It's a singleton so you access it with a static getter that will instantiate it if needed :

@objc public static func getSnapcall() -> Snapcall

Request the microphone access

  Snapcall.getSnapcall().requestPermission((callback: { result in
    if (result) {
      // microphone permission granted.
    }
  })

On IOs you have to directly ask for permission to your user. We provide a method that check if the permission is granted , and make the request if not.

@objc final public func requestPermission(callback: @escaping((Bool)->Void))

parameters description
callback ((Bool)->Void)) Completion Handlers true as argument if user grant the permission

check the microphone access

  if (Snapcall.getSnapcall().isPermissionRequestGranted()) {
    // permission are granted
  }

allow you to check the status of the microphone access.

@objc final public func isPermissionRequestGranted() -> Bool

launch a call


  init() {
    let myButton = UIButton();
    myButton.addTarget(self, action: #selector(pressed(sender:)), for: .touchUpInside)
    view.addSubview(myButton);
  }

  @objc func pressed(sender: UIButton) {
    Snapcall.getSnapcall().launchCall(bidId, "88b3d0f3a44311e78f9b0ae03a1ae33f", parameter: nil)
  }

You can now try to launch a call :

@objc final public func launchCall(bidId : String, parameter : Snapcall_External_Parameter?)

parameters description
bidId: String the identifier you create on our dashboard
parameter: Snapcall_External_Parameter? parameters for a call or nil

Snapcall parameter

var param = Snapcall_External_Parameter()
 param.urlImage = "https://mydomaine.ext/awesomeImage.png"
 /* or */
 param.nameImage = "mybundleImage"

 param.callTitle = "Snapcall"
 param.externalContext = NSMutableDictionary() // "{id: 123456,fn: 'john',ln: 'doe',e: 'john@doe.com',...}"
 param.externalContext.setValue("123456","id")
 param.displayName = "John"
 param.displayBrand = "Doe"
 param.shouldReturn = true
 param.backgroundColor = UIColor.red
 param.textColor = UIColor .red
 param.fontDescriptor = UIFontDescriptor

When the call start you can see our default user interface. You can configure this interface with this class.

You can also replace this default interface.

properties type description
urlImage String URL of an image you want to display in the call User Interface
nameImage String bundle name of an image you want to display in the call User Interface
callTitle String The title that will be displayed on the top of the UI
externalContext NSMutableDictionary Data you can link to the call as a NSMutableDictionary
displayName String The name that will be displayed in the UI
displayBrand String The brand/family name that will be displayed in the UI
shouldReturn boolean Your user is able to navigate in your app during the call
backgroundColor UIColor The backGround color that will be displayed on the UI
textColor UIColor The text color that will be displayed on the UI
fontDescriptor UIFontDescriptor The font that will be displayed on the UI

Snapcall.AppName = String
Snapcall.callIconTemplate = Data?
Snapcall.preferredStatusBarStyle = nil

There is also some parameter that are set statically.

properties type description
AppName String name for callkit. This name will appear into callKit UI.
callIconTemplate Data the icon that will be displayed by callKit
preferredStatusBarStyle UIStatusBar status bar style

Listen call event

Create the communication object


let client : SCClient = SCClient()

You can get get the different event coming from the call in your app. For that we provide the SCClient class that will connect to the Service hosting the call.

public init()

Event


class SnapcallListener :  SCClientListener {

    let client : SCClient = SCClient()

    init() {
      super.init()
      client.setListener(listener : self)
    }

    func onConnectionReady(_ parameter : SCClientEvent){}

    func onCreated(_ parameter : SCClientEvent){}

    func onRinging(_ parameter : SCClientEvent){}

    func onAnswer(_ parameter : SCClientEvent){}

    func onInternetDown(_ parameter : SCClientEvent){}

    func onInternetUP(_ parameter : SCClientEvent){}

    func onHangup(_ parameter : SCClientEvent){}

    func onHeld(_ parameter : SCClientEvent){}

    func onUnheld(_ parameter : SCClientEvent){}

    func onConnectionShutDown(){}

    func onMuteChange(_ parameter : SCClientEvent){}

    func onSpeakerChange(_ parameter : SCClientEvent){}

    func onUIRequest(_ parameter: SCClientEvent){}

    func onTime(_ parameter : SCClientEvent){}

    func onMessage(callID: String, message : Any){}

  }
Event Name description
onConnectionReady connected to the service, you can start to use the SCClient api.
onCreated The Call has been successfully created and is about to start.
onRinging sound start , user entering the queue and wait for answer.
onAnswer call has been answered. When calling Agent you can get the agent mail here. called again if an agent transfer occurs.
onInternetDown a network disconnection has been detected, we will try to reconnect.
onInternetUP network back, we reconnect the call.
onHeld agent put the call on hold.
onUnheld agent removed the hold.
onMuteChange the mute status has change.
onSpeakerChange the speaker has been activated or de activated.
onTime called every second , the time value will be updated.
onHangup the client or the agent hangup, call is going to end.
onConnectionShutDown no more call the service has shutdown.
onMessage You receive a message from your agent.
onUIRequest when UI is requested.

In each event you receive a SCClientEvent that represents the phone and call state.

Event parameter

hardware state - SCClientEvent

func onNewEvent(parameter: SCClientEvent) {
  let isMute = parameter.isMute()
  let isSpeaker = parameter.isSpeaker()
  let isConnected = parameter.isConnected()
  updateUI(isMute, isSpeaker, isConnected)
  if let call = parameter.getCall() {
    getCallParameter(call)
  }
}

public struct SCClientEvent public class objc_SCClientEvent : NSObject

The SCClientEvent allow you to get the network status , the microphone status and the audio output.

public func isMute() -> Bool : return true if the cal is mute.

public func isSpeaker() -> Bool : return true if the speaker is active false if you use headphone or call speaker.

public func isConnected() -> Bool : return the current Network status.

public func getCall() -> SCCall : return the call object, can be null.

The objc_SCClientEvent is the objectiveC version of this class.

Call state - SCCall


 func getCallParameter(call: SCCall) {

   let uniqueID: String = call.getCallIdentifier()
   let displayName: String? = call.getDisplayName()
   let displayBrand: String? = call.getDisplayBrand()
   let currentCallState: String? = call.getCurrentCallState()
   let time: Int = call.getTime()
   let data: Date = call.getStartedDate()
   let duration: Int = call.getDuration()
   let held: Bool = call.isHeld()
   let agent: String? = call.getAgentMail()
   let transferred : Bool = call.isTransferred()
 }

func getCallIdentifier() -> String : return an unique identifier for your call.

public getCurrentCallState() -> String? : return the state of the call. the state can be one of the five defined in SCClient :

STATE description
let CREATED : String = "Created" the call has been created
let DISCONNECTED: String = "Disconnected" an error has occurred we try to reconnect
let RINGING: String = "RINGING" sound start, waiting for answer
let CONNECTED: String = "connected" the call is running
let ENDED: String = "Ended" the call hangup

func String getDisplayBrand() -> String? the Name you submit for display in the dashboard or in the snapcall parameters

func getDisplayName() -> String? the brand you submit for display in the dashboard or in the snapcall parameters

func long getStartedTime() -> Int return the number of second since start (s) or -1

func long getDuration() -> Int return the number of second since call start (s) or -1

func getDate() -> Date? return the date at call start

func isHeld() -> Bool return true if call is on hold.

func getAgentMail() -> String? return the agent mail.

func isTransferred() -> Bool return true if the call has been transferred to a new agent.

func storeCall(SCCall call) {
  let encoder = JSONEncoder()
  let encoded = try encoder.encode(call)
  db.store(encoded);
}

func getCallFromDB() {
  let decoder = JSONDecoder()
  let call : SCCall = try decoder.decode(SCCall.self, from: db.get())
}

this class implement the codable protocol that allow you to save it in a file or a db. You are able to directly store this object and get it back when needed.

make your own user Interface

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

  Snapcall.defaultUserInterfaceOff = true
}

All this Event allow you to make your own user interface. The boolean defaultUserInterfaceOff allow you to block the default UI in order to make your own.

SCClient API

properties

public static let CREATED : String = "Created" state at start.

public static let DISCONNECTED: String = "Disconnected" state during network error.

public static let RINGING: String = "RINGING" state when sound start.

public static let CONNECTED: String = "connected" state when call is answered .

public static let ENDED: String = "Ended" state at hangup.

setListener

class myListener : SCClientListener
public init() {

  super.init();

  client.setListener(self);
}

public func setListener(listener : SCClientListener)

argument type description
listener SCClientListener an object that implement the SCClientListener protocol to receive call event
throws description
return description

the function objc_setListener allow to set the listener from the objectiveC.

hangup

do {
   try client.hangup();
} catch {
 print("snapcallNotStarted")
}

public func hangup() throws

argument type description
throws description
snapcallError.SnapcallNotStarted if the client is not connected
return description

mute

do {
   try client.mute();
} catch {
 print("snapcallNotStarted")
}

public func mute() throws

argument type description
throws description
snapcallError.SnapcallNotStarted if the client is not connected
return description

setSpeaker

do {
   try client.setSpeaker();
} catch {
 print("snapcallNotStarted")
}

public func setSpeaker() throws

argument type description
throws description
snapcallError.SnapcallNotStarted if the client is not connected
return description

sendInfo

do {
   try client.setSpeaker();
} catch {
 print("snapcallNotStarted")
}

public func sendInfo(message : Dictionary<String, Any>) throws -> Int

argument type description
throws description
snapcallError.SnapcallNotStarted if the client is not connected
return description
Int message ID , -1 if message as not been send

public func sendInfo(message : NSMutableDictionary) throws -> NSNumber

is the objectiveC version of send info.

getCurrentClientEvent

do {
   let event: SCClientEvent? = try? client.getCurrentClientEvent();
} catch {
 print("snapcallNotStarted")
}

public func getCurrentClientEvent() throws -> SCClientEvent

argument type description
throws description
snapcallError.SnapcallNotStarted if the client is not connected
return description
SCClientEvent the current event

public func objc_getCurrentClientEvent() throws -> objc_SCClientEvent

is the objective c version

rateCall

do {
  let event: SCClientEvent? = try? client.rate(4, { error?, result in
    if !error && result {
      // rate success
    }
  });
} catch {
  print("snapcallNotStarted")
}

public func rateCall(rate: Int, requestCallBack : (( _ error: Error?, _ : Bool) -> Void)?)

rate the last call made

argument type description
rate Int rating between 1 and 5
requestCallBack (( _ error: Error?, _ : Bool) -> Void)? a callback function to get the result of the request
throws description
return description

rateCall with SCCall

do {
  SCCall toRate = getUnratedCall(id);
  let event: SCClientEvent? = try? client.rate(toRate, 4, { error?, result in
    if !error && result {
      // rate success
    }
  });
} catch {
  print("snapcallNotStarted")
}

public func rateCall(call: SCCall?, rate: Int, requestCallBack : (( _ error: Error?, _ : Bool) -> Void)?)

argument type description
call SCCall? the call to rate
rate Int rating between 1 and 5
requestCallBack (( _ error: Error?, _ : Bool) -> Void)? a callback function to get the result of the request
throws description
return description

public func rateCall(call: objc_SCCall?, rate: Int, requestCallBack : (( _ error: Error?, _ : Bool) -> Void)?)

objectiveC version

Snapcall API

enum

snapcallError

serverError the server returned a bad status code

requestError the request has failed

badParameter parameter provided are false

permisionError the microphone permission has not been granted

properties

static var defaultUserInterfaceOff : Bool block the default UI

static var AppName: String? configure the name in callkit

static var preferredStatusBarStyle: UIStatusBarStyle? status bar style for the default user interface

static var callIconTemplate : Data? icon pour callkit

getSnapcall


  let snapcall = Snapcall.getSnapcall()

@objc public static func getSnapcall() -> Snapcall

argument type description
throws description
return description
Snapcall an instance of snapcall

releaseSnapcall


    let snapcall = Snpcall.releaseSnapcall()

release reference held by snapcall

@objc public static func releaseSnapcall()

argument type description
throws description
return description

buttonIsClosed


  Snapcall.getSnapcall().buttonIsClosed(bid_id: "88b3d0f3a44311e78f9b0ae03a1ae33f", snapcallCallBack:{ closed in

      })

this function check that a call can be made. If you block your button on our dashboard closed will be true, so you can prevent to display a button to launch the call.

@objc final public func buttonIsClosed(bid_id: String, snapcallCallBack : @escaping ((_ : Bool)->Void)) -> Bool

argument type description
bid_id String the button identifier to test
snapcallCallBack ((_ : Bool)->Void)) -> Bool callback with true as parameter if you can't make a call
throws description
return description
Bool if the request has failed before to be sent

launchCall

  Snapcall.getSnapcall().launchCall("88b3d0f3a44311e78f9b0ae03a1ae33f", nil)

launch a call

final public func launchCall(bidId : String, parameter : Snapcall_External_Parameter?)

argument type description
bid_id String the button identifier to test
parameter Snapcall_External_Parameter? parameter for the call
throws description
return description

restorCallUI

  Snapcall.getSnapcall().restorCallUI()

put the UI on front

@objc final public func restorCallUI()

argument type description
throws description
return description

requestPermission

Snapcall.getSnapcall().requestPermission((callback: { result in
  if (result) {
    // microphone permission granted.
  }
})

request the microphone permission

@objc final public func requestPermission(callback: @escaping((Bool)->Void))

argument type description
callback @escaping((Bool)->Void) callback to get the request result
throws description
return description

isPermissionRequestGranted

if (!Snapcall.getSnapcall().isPermissionRequestGranted()) {
  // microphone access has not been granted
}


check if the permission has been granted

@objc final public func isPermissionRequestGranted() -> Bool

argument type description
throws description
return description
Bool true if microphone access is granted

React-Native

Getting Started

This react-native package is a wrapper for the Snapcall native IOs and Android library to add VOIP functionality to a mobile application.

It is comported of :

OS/Hardware requirements

Android :

IOs :

Created with react-native version 0.60 on MacOs. Android sdk created with Android Studio 3.1.3 . IOs sdk created with Xcode 11.

Currently tested on : Xcode 10 android studio 3.1.3 react-native 0.60

Import

In your package.json add Snapcall dependency :

"react-native-snapcall": "^1.2.3"

Run yarn install react-native-snapcall to install .

snapcall work with auto linking from react native 0.60

IOs

Cocoapod verify that the Snapcall Swift framework has been installed with Cocoapod :

cd ios && pod install You will see the output: Installing Snapcall_Framework (5.5.1) Installing GoogleWebrtc Installing react-native-snapcall (1.2.2)

In case of build failure about header, or custom build, the path for the header from the snapcall can need to be set. For that go in the project setting and fix the path header variable in the build setting, it should reference the directory containing Snapcall_Framework.Framework.

if the pod is not automatically added add it :

pod 'Snapcall_Framework', '5.5.1'

Carthage [→]

Add this line to your Cartfile.

github "snapcall/snapcall_sdk_ios" == 5.5.1

add plist entries :

Build rules :

Android

android {
  ...
  compileOptions {
        sourceCompatibility = '1.8'
        targetCompatibility = '1.8'
    }
  }

On Android we compile with java 8 support , you will need to add this line to your app.graddle file under android. The library use androidx starting version 1.2.2

If this error occurs : 'Only Jar-type local dependencies are supported.' The android gradle version is too old and doesn't support AAR files, and needs to be updated.

Update your top-level gradle file on your project

buildscript {
  repositories {
      maven {
          url 'https://maven.google.com'
      }
      jcenter()
  }
  dependencies {
      classpath 'com.android.tools.build:gradle:3.1.3'
      // NOTE: Do not place your application dependencies here; they belong
      // in the individual module build.gradle files
  }
}

Error

In the eventual case of an error or configuration issue please feel free to open an issue in the git repository.

Use

In the next part BID is the Button identifier from Snapcall. It identifies your account and the destination of your call.

import

In your javascript file :

  import {Snapcall, SnapcallParameter} from 'react-native-snapcall';

Make a call

  var snapcall = new Snapcall();
  snapcall.askForPermission('Androidreason', 'Androidmessage')
  var parameter = new SnapcallParameter();
  function onPressCallButton() {snapcall.launchCallBid("bid", parameter)};

Check for button availability

  snapcall.bidIsClosed(bid, (res)=>{
    if (!res)
    {
      showCallButton();
    }
    })

Get Audio Permission

Android Permission is required before the launch of the first call therefore the library will throw an error. iOS will ask automatically if not granted.


  snapcall.askForPermission(Androidreason, Androidmessage).then((res)=>{
    if (res == "granted")
      console.log("permission granted");
    else
      console.log("permission refused");
  }).catch(()=>{
    console.log("permission error");
  })

Release snapcall ressource

Instance of the snapcall main native class keeps a reference on itself. If you want to release it, call this function.

  snapcall.releaseSnapcall()

Restor UI of call during a snapCall Call

  Snapcall.restorCallUI()

When a call is started and your user navigates in the app you can place default Call UI to the front at any moment.

Follow the Snapcall call Event

To monitor if a call is processing you can add event listener

 _function = function(eventParameter){
   console.log(content);
 }
 snapcall.addEventListener(eventName, _function)

Event list :

function name description
onConnectionReady the call api is ready to use
onCreated the call has been created successfully
onRinging call wait for an answer
onAnswer call has been answered
onInternetDown a network error occured , we try to recnnect
onInternetUP network came back
onHangup The call has ended
onMuteChange the mute status has change
onSpeakerChange the speaker has been activated/deactivated
onHeld agent has put the call on hold
onUnheld agent removed the hold
onTime update the timer
onConnectionShutDown Snapcall terminates all actions and is going to stop

Event parameter

  function onEvent(snapcallEvent) {
     const newState = {
      connected: snapcallEvent.connected,
        mute: snapcallEvent.mute,
        speaker: snapcallEvent.speaker,
        call: {
          callState: snapcallEvent.call.callState,
          transferred: snapcallEvent.call.transferred,
          displayName: snapcallEvent.call.displayName,
          duration: snapcallEvent.call.duration,
          displayBrand: snapcallEvent.call.displayBrand,
          agentMail: snapcallEvent.call.agentMail,
          time: snapcallEvent.call.time,
          held: snapcallEvent.call.
       }
      }
      this.setState(newState);
    }

For each event come with an object describing the current sate of the call and the of the phone.

name type description
connected boolean true if network is connected
mute boolean true if the is muted
speaker boolean true if speaker are active
call.callState String state for the call. State are described in Snapcall
call.transferred boolean true if the call has been transferred
call.displayName String name given in the parameter or on the dashboard
call.displayBrand String brand name given in the parameter or on the dashboard
call.duration int time since startup
call.agentMail String if it's an agent that answer , his email
call.time int time since call start
call.held boolean if the agent has put the call on hold

Customize Call

The SnapcallParameter Class

  var parameter = new SnapcallParameter();
  parameter.externalContext = {id: 123456,fn: 'john',ln: 'doe',e: 'john@doe.com',...};
  parameter.displayBrand = "Jhon";
  parameter.displayName = "Doe";
  parameter.callTitle = "Snapcall";
  parameter.urlImage = "https://mydomaine.ext/awesomeImage.png";
  parameter.textColor = "#FF0000";
  parameter.backgroundColor = "#00FF00";
  parameter.shouldReturn = false;
  parameter.android_AssetPathImage = "images/img.png";
  parameter.android_AssetPathFont = "fonts/font.otf";
  parameter.iOS_AssetPathImage = "bundleImg";
  parameter.iOS_AssetPathFont = "font";
  parameter.androidResimage = "images_img2snapcall";
  parameter.showBackButton = true;

most of the parameter are for the default User inteface

parameters description
externalContext Data you can link to the call
displayBrand The brand/family name that will be displayed in the UI
displayName The name that will be displayed in the UI
callTitle The title that will be displayed on the top of the UI
urlImage URL of the image that will be displayed in the UI
shouldReturn Your user will be able to navigate in your app during the call
backgroundColor The background color that will be set in the Snapcall Call UI
textColor The text color that will be set in the snapcall call UI
android_AssetPathImage The background image that will be displayed in the Snapcall call UI (from asset directory)
android_AssetPathFont The font that will be displayed in the Snapcall call UI (from asset directory)
iOS_AssetPathImage The background image that will be displayed in the snapcall call UI (from xasset directory)
iOS_AssetPathFont The font that will be displayed in the snapcall call UI (add your custom font to your plist)
androidResimage image in the res folder without extension
showBackButton when you want a return button in the default UI

As our default user interface is not done in javascript, Image from the javascript bundle are not directly used. For android, image will be added at the end in the res folder , the name will be prefixed by the path with _ in place of /. For debug you can provide the url on your developement server. For iOS will have to put your asset in the xasset folder. we don't make request on non secure url.

Static parameter

For IOs you can set some static variable for callkit in your appDelegate in objectiveC:


       [Snapcall setAppName:<#(NSString * _Nullable)#>];
       [Snapcall setCallIconTemplate:<#(NSData * _Nullable)#>];

App name is the name displayed in callkit. Call Icon is the icon displayed in callkit for your app.

Build your own UI

export default class App extends Component<Props> {
  ...
  state {
    inCall : false,
  }
  onCallEvent(ev) {
    this.setState({inCall : true})
  }
  onHangup (ev) {
    this.setState({inCall : false})
  }

  init {
    snapcall.activeDefaultInterface(false).then(()=>{}).catch(()=>{});
  }
  constructor (Props) {
    this.init()
    const myCallEventListener = onCallEvent.bind(this);
    const onHangupEventListener = this.onHangup.bind(this);
    snapcall.addEventListener (...
  }


  render() {
    if (this.state.inCall) {
      return  (
      <View style={styles.container}>
      <Button
      onPress={snapcall.hangup()}
      title="hangup"
      />
      </View>);
    }
    return  (
    <View style={styles.container}>
    ...
    <Button
    onPress={this.launchCall}
    title="clicktoCall"
    />
    ...
    </View>);
  }
}

All the different event and parameter allow you to build an UI that exactly fit your need. Here you can see a basic example about launching a call and hangup it without launching the snapcall UI.

The function activeDefaultInterface indicate to the library that you provide an UI. You can show it as soon as you receive event from the call. The API function about call control, like hangup, will be available.

Restor custom UI from background (Android Only)

public class MainActivity extends ReactActivity {

@Override
    protected void onPause() {
        super.onPause();
        Intent restor = new Intent(getApplicationContext(), MainActivity.class).addFlags(FLAG_ACTIVITY_NEW_TASK);
        RNSnapcallReactModule.setCustomReloadIntent(restor);
    }

    @Override
    protected void onResume() {
        super.onResume();
        RNSnapcallReactModule.setCustomReloadIntent(null);
    }
}

During a snapcall call there is an android notification present. When the user click on this notification it should be normal to restor the UI. To restor your UI you may need to set an intent from the java. In your react-native project in the android folder there is two class by default. an app and an Activity, you will need to add the function on the left to the activity. This function will send the app to the front if in background state. The flag FLAG_ACTIVITY_NEW_TASK is mandatory to launch an activity from the background.

Snapcall API

properties

STATE_CREATED = "STATE_CREATED"; state at call start

STATE_CONNECTED = "STATE_CONNECTED"; state when call answer

STATE_RECONNECT = "STATE_RECONNECT"; state when a network error occur

STATE_TERMINATED = "STATE_TERMINATED"; state at call hangup

addEventListener


  onAnswer(event) {
    this.setState(event);
  }

  init() {
    snapcall.addEventListener("onAnswer", this.onAnswer.bind(this));
  }

addEventListener(eventName, _function)

argument type description
eventName String the event to subscribe.
_function function the function called for the event.
return description

removeEventListener


  onAnswer(event) {
  }

  init() {
    snapcall.removeEventListener("onAnswer", this.onAnswer);
  }

remove occurence of a listener

removeEventListener(eventName, _function)

argument type description
eventName String the event to subscribe.
_function function the function called for the event.
return description

restorCallUI


  onClick() {
    snapcall.restorCallUI();
  }

put the default call user interface to front

restorCallUI()

argument type description
return description
promise resolved if we successfully make the request

activeDefaultInterface


  init {
    snapcall.activeDefaultInterface(false).then(()=>{}).catch(()=>{});
  }

block the call default user interface. You are able to make your own with the event provided.

activeDefaultInterface(value)

argument type description
value boolean false to block the default interface
return description
promise resolved if we successfully make the request

launchCallBid


  onClick {
    snapcall.launchCallBid("88b3d0f3a44311e78f9b0ae03a1ae33f", null).then().catch();
  }

launch a call with snapcall

launchCallBid(bid_id, parameter)

argument type description
value boolean false to block the default interface
return description
promise resolved if we successfully start the call rejected if microphone permission is not granted

bidIsClosed


  onClick {
    snapcall.bidIsClosed("88b3d0f3a44311e78f9b0ae03a1ae33f").then((value)=>{
      if (value){
        // call can be made
      }
    }).catch(/*error in the request*/);
  }

check if a call can be made

bidIsClosed(bid)

argument type description
bid String the call identifier you want to check
return description
promise resolved with a boolean as argument set to true if call can start, catch in case of error

getCurrentState


  init(){
    snapcall.getCurrentState().then(this.onEvent.bind(this)).catch(()=>{
      console.log("snapcall not started")
    });
  }

get the current call Event. Use it when restoring your UI

getCurrentState()

argument type description
return description
promise resolve with a event object as argument or rejected in case of error

rateLastCall


  init(){
    snapcall.rateLastCall(4).then(()=>{
        // success in request
    }).catch(()=>{
      //error with request
  })

add a rate to the last call you made.

rateLastCall(rate)

argument type description
rate int the rate between 0 and 5
return description
promise resolve if successfull or rejected in case of error

hangup


  init(){
    snapcall.hangup();

    //event hangup will be fired.
  }

when you want to hangup the call.

hangup()

argument type description
return description
promise resolve if successfull or rejected in case of error

setSpeaker


  init(){
    snapcall.setSpeaker();

    //event speakerChange will be fired.
  }

when you want to put the audio on the speaker or to remove it.

setSpeaker()

argument type description
return description
promise resolve if successfull or rejected in case of error

mute


  init(){
    snapcall.mute();

    //event muteChange will be fired.
  }

when you want to mute or unmute the call.

mute()

argument type description
return description
promise resolve if successfull or rejected in case of error

askForPermission


  init(){
    snapcall.askForPermission("make a call", "authorize the microphone access to contact our support").then((res)=>{
      if (res == "granted") {
        // permisson granted
      }
    });
  }

ask to the user the microphone access

askForPermission(Androidreason, Androidmessage)

argument type description
Androidreason String title to explain your request
Androidmessage String body to explain your request

the argument are set for ios into the plist file.

return description
promise resolve with "granted" has argument if successfull or "denied" if not, rejected in case of error

releaseSnapcall


  onEnd(){
    snapcall.releaseSnapcall()
  }

remove the reference held by snapcall.

releaseSnapcall()

argument type description
return description

Test Application

In the _test repository you can find a sample app. To test the sample APP android studio and xcode are needed, as well as yarn/npm ,cocoapod(ruby), node, and react-native(cli - link).

If you use yarn after Adding the module remove the _test directory from /node_module/react-native-snapcall.

yarn install
cd iOS && pod install && cd ..
react-native link
open -a xcode ios/TestCall.xcworkplace
open -a 'android studio' android/
react-native start

Basic example

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow
 */

import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, Button} from 'react-native';
import {Snapcall, SnapcallParameter} from 'react-native-snapcall';

const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
  android:
    'Double tap R on your keyboard to reload,\n' +
    'Shake or press menu button for dev menu',
});

const parameter = new SnapcallParameter();
const snapcall = new Snapcall();
type Props = {};
export default class App extends Component<Props> {
    launch(){

    snapcall.launchCallBid("0999b36e472111e997380ae222c5da84", parameter);
    }
  render() {
snapcall.activeDefaultInterface(false);
snapcall.askForPermission("voip", "voip");
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>Welcome to React Native!</Text>
        <Text style={styles.instructions}>To get started, edit App.js</Text>
        <Text style={styles.instructions}>{instructions}</Text>
        <Button title="hangup" onPress={snapcall.hangup}/>
        <Button title="launch" onPress={this.launch}/>
    </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

this code sample is a basic app.js from an empty project. It block the default UI and allow to launch call and hangup.