Voice SDKs
...
Introduction and Glossary
Android SDK Integration
36 min
getting started software package the exotelvoice sdk software package includes android aar file integration guide sample application for reference javadoc for api reference subscriber management api documentation add exotel voice sdk library to project method i open your android application code in android studio choose file > new > new module > import exotel voice release aar package provide the path to the downloaded aar file add the sdk as a dependency for the app go to file > project structure > dependencies > “+” that says “all dependencies > add path of aar add exotel voice sdk as dependency to your application method ii copy the aar file to the libs folder and edit the build gradle file to include implementation project(path '\ exotel voice sdk') below contents part of settings gradle file include '\ app', '\ exotel voice sdk' implementation filetree(include \[' jar'], dir 'libs') add the following libraries to the build gradle file implementation 'com google code gson\ gson \[2 8 5]' implementation 'dnsjava\ dnsjava \[2 1 9,)' implementation 'com squareup okhttp3\ okhttp \[3 14 2,)' implementation 'com squareup okhttp3\ logging interceptor \[3 14 2,)' target platform supported android version 10 0+ supported android abis armeabi v7a, arm64 v8a, x86 64 and x86 permissions \<uses permission android\ name="android permission internet" /> \<uses permission android\ name="android permission access network state" /> \<uses permission android\ name="android permission broadcast sticky" /> \<uses permission android\ name="android permission record audio" /> \<uses permission android\ name="android permission modify audio settings" /> \<uses permission android\ name="android permission read phone state" /> \<uses permission android\ name="android permission wake lock" /> \<uses permission android\ name="android permission disable keyguard" /> \<uses permission android\ name="android permission vibrate" /> \<uses permission android\ name="android permission foreground service" /> \<uses permission android\ name="android permission bluetooth"/> \<uses permission android\ name="android permission manage own calls"/> \<uses permission android\ name="android permission post notifications"/> \<uses permission android\ name="android permission bluetooth admin"/> \<uses permission android\ name="android permission bluetooth connect"/> //required for android 14 onwards \<uses permission android\ name="android permission foreground service phone call" /> \<uses permission android\ name="android permission foreground service microphone" /> proguard rules the proguard rules for exotelvoice are included in the aar file these rules will be auto included when the proguard is enabled for your application build android service the application needs to integrate exotelvoice in a service this should be run as a foreground service when a call is in progress so that the application is not killed when it moves to the background when the call is over, service should be moved to the background refer to voiceappservice in the sample application use foreground service types when setting up the foreground service, define the foregroundservicetype attributes as phonecall and microphone in the manifest this will indicate that the service is responsible for handling calls and microphone access, preventing unnecessary security issues related to background access for instance \<service android\ name=" voiceappservice" android\ foregroundservicetype="phonecall|microphone" android\ permission="android permission foreground service" android\ exported="false" /> this setup ensures that the service continues running during an active call and has the necessary permissions to access the microphone additionally, after the call ends, the service can be moved to the background to release unnecessary system resources managing microphone permission according to android 14 guidelines android 14 enforces strict restrictions on background services, particularly for microphone access to comply with these guidelines, apps must manage foreground services effectively, ensuring they are correctly configured for both incoming and outgoing calls this ensures smooth call functionality while maintaining security and privacy when the foreground service is started, it should begin with foreground service type phone call this allows the app to operate in the background while displaying the incoming call notification servicecompat startforeground(this, notification id, notification,serviceinfo foreground service type phone call); incoming calls when the app is closed and an incoming call is detected, it should first start as a normal service to handle initializing the sdk once the user got the onincomingcall event from the sdk and trying to answer the call, so before invoking the answer method of the sdk, the application must be in foreground and the service type must be updated to enable microphone access during the call this will ensure that app will work seamlessly when the app is running in the background or is closed , public void answer() throws exception { voiceapplogger debug(tag, "answering call"); if (null == mcall) { string message = "call object is null"; throw new exception(message); } try { updateforegroundservicetype(mcall, callstate answering); toneplayback stoptone(); mcall answer(); } catch (exception e) { throw new exception(e getmessage()); } voiceapplogger debug(tag, "after answering call"); } outgoing calls after dialing the call from the app the user will get the oncallinitiated() callback once the callback is received, the service should be updated to enable microphone access for the call note that , while updating the foreground service with microphone , application must be in foreground it will ensure that , if during call, app goes into background then voice will flow properly @override public void oncallinitiated(call call) { voiceapplogger debug(tag, "on call initiated"); for (callevents callevents calleventlistenerlist) { callevents oncallinitiated(call); } mcall = call; updateforegroundservicetype(call,callstate outgoing initiated); voiceapplogger debug(tag, "end oncallinitiated"); } update foreground service type function the updateforegroundservicetype method is responsible for updating the foreground service type based on the current call state it ensures compliance with android 14's guidelines for background services and microphone access private void updateforegroundservicetype(call call, callstate outgoinginitiated) { handler post(new runnable() { @override public void run() { notification notification = utils createnotification(outgoinginitiated, call getcalldetails() getremoteid(), call getcalldetails() getcallid(), call getcalldetails() getcalldirection()); if (build version sdk int < build version codes tiramisu) { startforeground(notification id, notification); } else { startforeground(notification id, notification, serviceinfo foreground service type manifest); } } }); } initialize library exotelvoice interface class provides an entry point to initialize the voice library and set it up for handling calls // get exotel voice client exotelvoiceclient exotelvoiceclient = exotelvoiceclientsdk getexotelvoiceclient(); //set the event listener for sdk logs exotelvoiceclient seteventlistener(this); // set listener to handle events from voice client exotelvoiceclient seteventlistener(new exotelvoiceclientlistener() { // initialization success handler void oninitializationsuccess() { } // initialization failure handler void oninitializationfailure(exotelvoiperror exotelvoiperror) { } // logs reported by the voice library void onlog(loglevel loglevel, string tag, string message) { } // log upload success handler void onuploadlogsuccess() { } // log upload failure handler void onuploadlogfailure(exotelvoiperror exotelvoiperror) { } // authentication failure handler void onauthenticationfailure(exotelvoiperror exotelvoiperror) { } }); sdk initialization requires a subscribertoken generated by the exotel platform workflow for this token generation and management is described in section authentication and authorization // request token from application backend (example) string subscribertoken = getaccesstoken(); the subscribertoken is provided to exotelvoice during initialization // initialize client library android content context context = this getapplicationcontext(); exotelvoiceclient initialize( context, // android application context hostname, subscribername, displayname, accountsid, // exotel account sid subscribertoken, ); client library notifies oninitializationsuccess() on success and oninitializationfailure() on failure on expiry of subscribertoken , the library will post onauthenticationfailure() at which application should request new subscribertoken from application backend and program in the initialize() api parameter description hostname https //miles apac sg exotel in/v2 subscribername param “subscriber name” returned as part of the subscriber management api to create a subscriber displayname subscriber name accountsid account sid param from api settings page in the exotel dashboard subscribertoken can be gotten from the api in the `get subscriber token` section in the subscriber management api document in the subscribertoken , both the refresh token and access token are base64 encoded subscriber token format example "subscriber token" { "refresh token" "eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9 eyjpc3mioijleg90zwwilcjzdwiioijbcmnoaxqilcjpyxqioje1nzy2ndc5otksimv4cci6mtu3njc0nzk5oswiy2xpzw50x2lkijoinuuwntg2nuuifq hc3umvfflkipij8r9kcp9o9he9he51le08ro22u7eqs", "access token" "eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9 eyjpc3mioijleg90zwwilcjzdwiioijbcmnoaxqilcjpyxqioje1nzy2ndc5otksimv4cci6mtu3njc0nzk5oswic2nvcguioij2b2ljzsisimnsawvudf9pzci6ijvfmdu4njvfin0 uqd ahgbit4xqtpfl 567dmjqrklmltpag0kpgb2qma" } below are the sample methods to get the subsribertoken, customers can implement this in their own method use the below http apis to get the subscriber token and convert subscriber token to jobject and pass it to exotelvoiceclient initialize(); note copying manually generated subscriber token has issues best way is to make api calls in source code and pass them by converting to jobject to the exotelvoiceclient initialize(); pass the proper device id to the login api to avoid invalid refresh token error build gradle should be proper complete working build gradle file is attached in the link https //github com/exotel/exotel voip sdk android/blob/main/build gradle device id is the actual device id in which the app is running using below sample code string androidid = settings secure getstring (context getcontentresolver(), settings secure android id ); generate access token refer “subscriber management api” documentation section 3 1 for creating subscriber token managing calls after successful initialization, the library is ready to handle calls dialing out calls or receiving incoming calls is managed through callcontroller interface // get call controller interface callcontroller callcontroller = exotelvoiceclient getcallcontroller(); call progress events are notified to the application through the calllistener interface attached to the callcontroller object // attach call listener callcontroller setcalllistener(new calllistener() { // handler for incoming call alert void onincomingcall(call call) { } // handler for outgoing call initiated event void oncallinitiated(call call) { } // handler for call ringing event for outgoing call void oncallringing(call call) { } // handler for call connected event void oncallestablished(call call) { } // handler for call termination event void oncallended(call call) { } // handler for missed call alert void onmissedcall(string remoteid, date time) { } }); each call object provides a call interface to manage the call lifecycle and perform operations like answer incoming calls, hangup calls, mute, unmute, and get call statistics // mute call mute(); // terminate call call hangup(); make outgoing call to make outgoing calls, the callcontroller interface must be created and a listener interface is attached as mentioned in section managing calls provide sip exophone number in dial() api of callcontroller interface to make outgoing calls // get call controller interface callcontroller callcontroller = exotelvoiceclient getcallcontroller(); // attache call progress listener callcontroller setcalllistener(new calllistener() { } // dial out call to remoteid call call = callcontroller dial(\<sip exophone>, \<custom field>); params for dial method param mandatory description sip exophone yes exotelvoice library always routes the call via sip exophone configured for your account sip exophones are different from the pstn / mobile exophones they can be identified using sip prefix contact exotel support to enable sip exophone in your account similar to the number masking workflow, the application backend must maintain the call context between caller and callee on receiving the call at sip exophone, the exotel platform will request the callee details from the application backend to bridge the call refer section dial whom endpoint for details custom field no the string that will be passed here will be sent to the dialwhom endpoint under the “customfield” param all alphanumeric characters are allowed along with comma `,` colon ` ` and all kinds of brackets {}()\[] once initiated, call progress events can be monitored through calllistener interface and call control operations can be performed using call interface receive incoming call the mobile application receives incoming call data in push notification sent by your application backend refer section push notification endpoint for details to handle the incoming call, the exotelvoice library should be in an initialized state refer to section initialize library after initialization, the callcontroller interface should be created and the listener be attached as mentioned in section managing calls // get call controller interface callcontroller callcontroller = exotelvoiceclient getcallcontroller(); // attache call progress listener callcontroller setcalllistener(new calllistener() { } the sample application uses firebase cloud messaging to push call data to the device once received at the mobile app, it is sent to exotlevoiceclient through relaysessiondata api public class voiceappfirebaseservice extends firebasemessagingservice{ public void onmessagereceived(remotemessage remotemessage) { map\<string,string> calldata = remotemessage getdata(); map\<string,string> pushnotificationdata = new hashmap<>(); pushnotificationdata put("payload", calldata get(“payload”)); pushnotificationdata put( "payloadversion", calldata get(“payloadversion”)); exotelvoiceclient relaysessiondata(pushnotificationdata); } } the voice library will process and validate the data and send an onincomingcall() event to the application with the call object application has to update their foreground service with microphone permission and it must be required to app in foreground mode to update the foreground service with microphone permission hence for this we can move application in foreground with ui element and then update the foreground service the application can call the answer() api to accept the incoming call // accept call call answer(); hangup call the application can call hangup() api on the call object to decline an incoming call // decline / terminate call call hangup(); audio management before the call begins, exotel’s sdk checks the current audio output mode and sets the audio route accordingly by default, it is in the earpiece mode for example, if a wired headset is plugged in, audio will get routed via wired headset speaker phone mode can be enabled and disabled using call object apis // change audio route to speaker call enablespeaker(); // change audio route to phone earpiece call disablespeaker(); bluetooth mode can be enabled and disabled using call object apis this will only work when any bluetooth device is connected to the phone // change audio route to bluetooth only works if a bluetooth device is connected to the phone call enablebluetooth(); // change audio route to phone earpiece call disablebluetooth(); current audio route can be fetched using call object api getaudioroute it will return callaudioroute enum i e earpiece, speaker, bluetooth, headphones // get current audio route call getaudioroute(); local mic can be muted and unmuted during in call using call object apis // mute the call call mute(); // unmute the call call unmute(); call state the call state can be accessed by calling getlatestcalldetails() getcallstate() this state can be displayed on the calling screen to let the user know about the state of the call refer to the call state flow diagram call state description call back recommended message to display on the calling screen none this state occurs when a call object is created but not yet initiated outgoing initiated this state is set when an outgoing call is initiated it’s set after dialing and before the call starts ringing connecting early indicates early media reception before the call is answered ringing the receiver’s phone is ringing but hasn’t been answered yet oncallringing() ringing connecting the call starts connecting connecting incoming this state is set when there’s an incoming call, and the system isn’t idle onincomingcall() caller id, custom information related to the callee answering occurs when the user accepts the incoming call; it’s a transitional phase before the call gets established answering established indicates that both parties have accepted the call, and communication is established oncallestablished() connected media disrupted occurs if there’s a disruption in media transmission during an ongoing call due to issues like rtp loss to handle such disruptions effectively ensuring continuity or termination based on severity it initiates reconnection procedures and moves to reconnecting if rtp loss persists to ensure calls remain active during temporary disruptions it monitors rtp resumption or port renewal activities and returns to established upon successful media restoration onmediadisrupted() reconnecting renewing media this state indicates that media (rtp) is resuming or renewing after being disrupted onrenewingmedia() reconnecting ending the process of ending or dropping an ongoing or established call starts here ended indicates that a call has ended completely, returning system status to idle oncallended ended this indicates that the media session has been cleaned up ondestroymediasession() call statistics application can query the call quality statistics periodically during the call by using getstatistics() api of the call object it provides info about codec and the call qos parameters like packet loss, jitter and round trip delay call details application can query the call details record of the call using getcalldetails() api of the call interface the cdr provides info on callid, call state, call duration, call end reason etc this api can be queried only for the latest call since sdk maintains only a single call context handling of pstn calls exotelvoice has following behavior during pstn calls when a pstn call is in progress, no outgoing calls are allowed by the voice client when a pstn call is in progress, any incoming voip call is automatically declined by the voice library and reported as a missed call to the application when a voip call is in progress and a pstn call lands on the phone, then the voip call continues if the user declines or does not respond to the pstn call if the user accepts the pstn call, then the voip call is automatically terminated by the voice client reporting problems the voice client library logs are stored in the application internal storage any issue along with the logs can be reported by app to exotel using uploadlogs() api of exotelvoice interface // upload logs with description from startdate and enddate exotelvoiceclient uploadlogs(startdate, enddate, description); the api triggers onuploadlogsuccess() or onuploadlogfailure() callback based on the success or failure of the operation reporting call quality feedback call quality can be queried from the user and reported to the exotel platform at the end of the call // upload logs with description from startdate and enddate int rating = 3 callissue issue = background noise call postfeedback(rating,issue); the rating is a quality score that can take values 1 to 5 5 excellent quality no issues 4 good quality negligible issues 3 average quality minor audio noise 2 bad quality frequent choppy audio or high audio delay 1 terrible unable to communicate call drop, no audio or one way audio the call issue is a descriptive explanation of the issue no issue no issues observed background noise low audio clarity due to noisy audio choppy audio frequent breaks in audio or garbling in audio high latency significant delay in audio no audio no audio received from far user echo echo during the call
🤔
Have a question?
Our knowledgeable support team and an awesome community will get you an answer in a flash.
To ask a question or participate in discussions, you'll need to authenticate first.