Using the Android SDK
Using the SDK
- Initialize the SDK
- Register Voice User
- SDK state
- Request runtime permissions
- Place a call
- Mid-call features
- Call updates
- Receive a Call
- Update contact information
- Audio control
- Update push token and phone number
- Unregister Voice User
- Proguard Rules
- Shared Preferences
Initialize the SDK
To initialize the Voice SDK add the following in the onCreate
function of your Activities and Services classes that need to use the Voice SDK APIs.
class MyService: LifecycleService() {
...
val voice by lazy { Voice.getInstance() }
override fun onCreate() {
super.onCreate()
voice.init(application)
...
}
...
}
Collect log information from the SDK
lifecycleScope.launch {
voice.loggedData.collect { voiceLog ->
// Process received log
}
}
The Voice SDK sends log information to the application.
Note: References to Voice in this document pertain to the
voice
object.
Register and Activate a Voice User
In order for the Voice SDK to function correctly, you need to add Voice user information to make and receive calls. Use the following for user data:
val userConfiguration = userConfiguration {
accountId = "YOUR_ACCOUNT_ID"
userId = "YOUR_USER_ID"
msisdn = "YOUR_DEVICE_NUMBER"
jwtToken = "YOUR_JWT_TOKEN"
displayName = "YOUR_NAME"
deviceId = "UNIQUE_DEVICE_IDENTIFIER"
}
Note:
*
msisdn
is optional.
*ThedeviceId
is the device unique identifier, preferably FirebaseInstanceId. Please refer to Work with instance IDs and GUIDs for more information.
The Voice SDK also requires application information in the setup. Use the following for application data:
val sessionConfiguration = sessionConfiguration {
applicationId = BuildConfig.APPLICATION_ID
baseUrl = "VOICE_BASE_URL"
pushToken = "YOUR_PUSH_TOKEN"
}
Note: The Voice URL is provided by the console when you request access to the Voice SDK.
Once you have the configurations ready, use them to activate the Voice SDK. For example:
configuration {
userConfiguration = userConfig
sessionConfiguration = sessionConfig
}
try {
voice.activate(configuration)
saveUserDataToPreference(userConfiguration)
} catch(e: RegistrationException) {
// Log the error
}
Upon activation, the application is ready to make and receive calls.
To check if the user is registered and activated, use
voice.isActivated()
Note: Use the result of this function to determine whether or not the user requires activation.
SDK state
The Voice SDK allows you to get updates about sdk state
. You can collect it using the following StateFlow
object:
lifecycleScope.launch {
voice.state.collect { state ->
// Handle the state
}
}
Request runtime permissions
The Voice SDK requires access to READ_PHONE_STATE
and RECORD_AUDIO
permissions in order to successfully place and receive calls. Apps targeting API level 31 or above will additionally need READ_PHONE_NUMBERS
permission. If you attempt to place or accept a call without the necessary permissions granted, one of the following exceptions will be delivered in the VoiceCallResult
: RTCException.PhoneAndMicPermissionDeniedException
, RTCException.PhonePermissionDeniedException
or RTCException.MicPermissionDeniedException
Place a call
In order to place a call, the Voice SDK requires the callee contact information. Use the VoiceContact
object from the Voice SDK to create the callee information.
Once the contact
object is ready you can place a call using the following:
when(val voiceCallResult = voice.placeCall(contact)) {
is VoiceCallResult.Success -> { /* handle the call */ }
is VoiceCallResult.Failure -> { /* handle the error */ }
}
Note: When the necessary Permissions are not provided, placing a call will fail with a
PermissionNotGranted
exception.
Mid-call features
The Voice SDK provides APIs to interact with an active VoiceCall
. These actions are:
accept()
reject()
endAndAccept()
hangup()
mute()
unmute()
hold()
resume()
You can also query the state of incoming calls such as isIncoming
, isPeerOnHold
, isMuted
, and callStartTime
.
The Voice SDK allows a user to have a second incoming call while a call is in progress. Updates are available via voice.voiceCallState
, which has the type of SharedFlow<VoiceCallState>
. The Voice SDK provides functionality to end the ongoing call and accept an incoming call using the endAndAccept
action. The default accept
behavior is to put the ongoing call on hold and accept the incoming call.
Call updates
In order to receive call updates use voice.voiceCallState
which delivers a VoiceCallState
object:
voice.voiceCallState.collect { voiceCallState ->
when(voiceCallState) {
is VoiceCallState.Added -> {
// Call recently added
}
is VoiceCallState.Failed -> {
// Call that failed with an exception.
}
is VoiceCallState.HoldUpdated -> {
// Call that has been moved on hold because of accepting another incoming call.
}
is VoiceCallState.Removed -> {
// Call failed or finished.
}
is VoiceCallState.Updated -> {
// Call that's recently updated.
}
}
}
Receive a call
The application needs to receive push notifications via FCM. If you are not using FCM on your project refer to this Firebase topic.
The application receives a push notification via the FirebaseMessagingService
object as an indication of an incoming call. Once the application receives the push notification, it needs to check if the notification is for an incoming call. Use:
val isVoiceNotification = voice.isVoiceNotification(data)
If the notification is for an incoming call, it is passed along with its context to the Voice SDK. To process an incoming call the Voice SDK starts a Foreground Service which requires a notification to be displayed by the application. The notification is displayed for a very short time and may display text such as Incoming call. This notification can be replaced by another notification with Accept and Reject actions as soon as updates are received.
If you wish to present a view as soon as the incoming call is connected, use:
voice.callActions.collect { callAction ->
when(callAction) {
CallAction.PRESENT_INCOMING_CALL -> {
// Show UI to present the incoming call to the user
}
CallAction.MUTE_INCOMING_CALL -> {
// Call the API to mute the ringing
}
}
}
Note: Start the collection before the
receiveCall
in order to present your notification or view.
Once the notification is ready, use the following:
when(val callResult = voice.receiveCall(data, notification)) {
is VoiceCallResult.Failure -> {
// Handle the error
}
is VoiceCallResult.Success -> {
// Handle the success
}
}
Update contact information
For any active call, you can update the contact information by using the following:
val contact = VoiceContact("CONTACT_ID", "CONTACT_NAME", "CONTACT_AVATAR_URL", "CONTACT_PHONE_NUMBER")
voice.updateContact(call.uuid, contact)
Audio control
The Voice SDK allows you to switch your audio output during a call by using the following:
voice.setVoiceAudioOption(audioOption)
In order to receive audio updates collect callAudioOptionUpdates
passes on VoiceCallAudioOption
:
lifecycleScope.launch {
voice.callAudioOptionUpdates.collect { voiceCallAudioOption ->
// Handle audio option
}
}
The SDK allows you to easily toggle between audio options. It follows the following:
VoiceCallAudioOption.EARPIECE
to VoiceCallAudioOption.SPEAKER
,
VoiceCallAudioOption.SPEAKER
to VoiceCallAudioOption.EARPIECE
,
VoiceCallAudioOption.BLUETOOTH
to VoiceCallAudioOption.EARPIECE
To toggle between audio options use:
voice.toggleAudioOption()
Note:
audioOption
is of the typeVoiceCallAudioOption
which consists of the values ofBLUETOOTH
,SPEAKER
, andEARPIECE
.
Update push notification token and phone number
After registration the push token and phone number that are provided during activation can be reconfigured.
To update the push token, add the following to the class which extends FirebaseMessagingService
:
override fun onNewToken(token: String) {
super.onNewToken(token)
...
when(val result = voice.updatePushToken(token)) {
is ResultWrapper.Success -> {/* Handle success */ }
is ResultWrapper.Error -> { /* Handle error */ }
}
}
To update the phone number, use the following:
when(val result = voice.updatePhoneNumber(phoneNumber)) {
is ResultWrapper.Success -> {/* Handle success */ }
is ResultWrapper.Error -> { /* Handle error */ }
}
Unregister and Deactivate Voice User
To deactivate or unregister a user, use the following method:
lifecycleScope.launch {
voice.deactivate()
}
Proguard Rules
If minifyEnabled
is set to true
in your application:
- Add the following in your
proguard-rules.pro
file:
#noinspection ShrinkerUnresolvedReference
-keep class com.eght.voice.sdk.** { *; }
-keep class com.eght.sip.** { *; }
-keep class com.eght.call.** { *; }
- If the compiler complains about
META-INF/*
file collision after adding the proguard rules, you must add the following to your app-levelbuild.gradle
:
android {
...
packagingOptions {
pickFirst '**'
}
}
Shared Preferences
In order to persist data, it is recommended that the Voice SDK is excluded from allowBackup. If your application needs the attribute android:allowBackup="true"
in your AndroidManifest.xml
file, we recommend doing the following:
Android 11 (API level 30) and lower
- Create an
xml
file under thexml
resource directory. We'll call itbackup_rules.xml
:
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<exclude domain="sharedpref" path="voice-sdk-preferences.xml"/>
<exclude domain="sharedpref" path="rtcData.xml"/>
</full-backup-content>
- Add the following attribute to your
AndroidManifest.xml
:
<application
android:fullBackupContent="@xml/backup_rules"
... >
...
</application>
Android 12 (API level 31) and higher
- Create an
xml
file under thexml
resource directory. We'll call itdata_extraction_rules.xml
:
<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
<cloud-backup>
...
<exclude domain="sharedpref" path="voice-sdk-preferences.xml"/>
<exclude domain="sharedpref" path="rtcData.xml"/>
...
</cloud-backup>
<device-transfer>
...
<exclude domain="sharedpref" path="voice-sdk-preferences.xml"/>
<exclude domain="sharedpref" path="rtcData.xml"/>
...
</device-transfer>
</data-extraction-rules>
- Add the following attribute to your
AndroidManifest.xml
:
<application
android:dataExtractionRules="@xml/data_extraction_rules"
... >
...
</application>
Note: The approaches must be combined for devices that are targeting API 31+ but have the minimum SDK set to a lower value.
Note: If you have
android:allowBackup="false"
, you do not need to add this file nor add thefullBackupContent
and/ordataExtractionRules
attribute(s).
Updated over 1 year ago