Android v3 migration guide
Voximplant Android SDK v3 has introduced major breaking changes to provide more functionality and improve the SDK scalability. This guide explains how to smoothly migrate the application code base to use the new API.
Contents
Add Voximplant SDK to your project
Voximplant Android SDK v3 comes with the BoM (Bill of Materials) that allows developers to manage the Voximplant libraries according to the application needs (i.e. import only those that are required).
Voximplant Android SDK v3 BoM contains the following modules:
com.voximplant.android-sdk-core— provides API to connect and authenticate the Voximplant users in an android application and manage audio devices.com.voximplant.android-sdk-calls— provides API to make, receive, and manage calls and conferencescom.voximplant.android-sdk-messaging— provides API to implement the chat functionalitycom.voximplant.android-sdk-renderer-compose— provides a composable function to renderer a video stream on an application UI built with Jetpack Compose
From v2 (Kotlin DSL):
implementation("com.voximplant:voximplant-sdk:2.42.0")
To v3 (Kotlin DSL):
implementation(platform("com.voximplant:android-sdk-bom:3.1.0"))
implementation("com:voximplant.android-sdk-core")
implementation("com.voximplant:android-sdk-calls")
implementation("com.voximplant:android-sdk-renderer-compose")
implementation("com:voximplant:android-sdk-messaging")
SDK initialization
Voximplant Android SDK is required to be initialized with the Android application context before any API method call. Use VICore.initialize API.
Executor on which all Voximplant Android SDK events are received is now created by the SDK as a single thread executor and may be manually configured via VICore.callbackExecutor property.
From v2:
val client = Voximplant.getClientInstance(
/* executor = */ Executors.newSingleThreadExecutor(),
/* context = */ applicationContext,
/* clientConfig = */ ClientConfig(),
)
To v3:
VICore.initialize(applicationContext)
Connect and authenticate the Voximplant users
The Client API is now the object and can be accessed from anywhere. The API to connect and log in the users are redesigned and provided with the callbacks with the extended error descriptions.
To establish the connection to the Voximplant cloud it is required to specify the node the Voximplant account belongs to via the ConnectOptions.
From v2:
import com.voximplant.sdk.client.AuthParams
import com.voximplant.sdk.client.IClient
import com.voximplant.sdk.client.IClientLoginListener
import com.voximplant.sdk.client.IClientSessionListener
import com.voximplant.sdk.client.Node
class SomeClass : IClientSessionListener, IClientLoginListener {
private lateinit var client: IClient
// ctor and other logic here
// Note: you also need to implement other methods from
// IClientLoginListener and IClientSessionListener
override fun onConnectionEstablished() {
Log.i(TAG, "Connection established")
client.login("myuser@myapp.myaccount.voximplant.com", "mypass")
}
override fun onLoginSuccessful(
displayName: String,
authParams: AuthParams?,
) {
Log.i(TAG, "Login succeeded")
}
fun connectToVoximplant() {
client.setClientSessionListener(this)
client.setClientLoginListener(this)
client.connect(Node)
}
}
To v3:
import com.voximplant.android.sdk.core.AuthParams
import com.voximplant.android.sdk.core.Client
import com.voximplant.android.sdk.core.ClientSessionListener
import com.voximplant.android.sdk.core.ConnectOptions
import com.voximplant.android.sdk.core.ConnectionCallback
import com.voximplant.android.sdk.core.ConnectionError
import com.voximplant.android.sdk.core.DisconnectReason
import com.voximplant.android.sdk.core.LoginCallback
import com.voximplant.android.sdk.core.LoginError
import com.voximplant.android.sdk.core.Node
class SomeClass {
val clientSessionListener = object : ClientSessionListener {
override fun onConnectionClosed(reason: DisconnectReason) {
Log.w(TAG, "Connection closed")
Client.setClientSessionListener(null)
}
}
fun connect() {
Client.connect(
options = ConnectOptions(node = Node), // Select Node from Voximplant Cloud
callback = object : ConnectionCallback {
override fun onFailure(error: ConnectionError) {
Log.e(TAG, "Connection failed – $error")
}
override fun onSuccess() {
Log.i(TAG, "Connection succeeded")
Client.setClientSessionListener(clientSessionListener)
// You can now log in
}
},
)
}
fun login() {
Client.login(
username = "myuser@myapp.myaccount.voximplant.com",
password = "mypass",
callback = object : LoginCallback {
override fun onFailure(error: LoginError) {
Log.e(TAG, "Login failed – $error")
}
override fun onSuccess(displayName: String, authParams: AuthParams?) {
Log.i(TAG, "Login succeeded")
}
},
)
}
}
Local video stream changes
Voximplant Android SDK v3 allows developers to create a local video stream outside an active call or conference. It simplifies the implementation of such functionality as video preview and camera configuration. A local video stream should be created based on a video source.
Each video support provides API for its configuration. The SDK provides support for the following video sources:
CameraVideoSource — allows developers to setup and manage cameras on the Android device
CustomVideoSource — allows developers to inject video frames created outside the SDK (on the application side) to a call or a conference
ScreenCaptureVideoSource — allows developers to capture the Android device screen
A video source starts producing frames (starts capturing) only if at least one the following conditions is met:
- a video renderer is added to the local video stream
- a local video stream is attached to an active call or a conference
If a video source requires a system permission, for example a camera permission, it is recommended to request the permission before creating a local video stream.
A video source stops producing frames (stops capturing) if both conditions are met:
- a local video stream is not rendered to any renderer
- a local video stream is not sent in any call or conference
If a video call or a conference has ended, but a local video stream still has any renderers, video source does not release its resources. It is important to carefully manage local video streams on the application side to avoid unexpected system indicators for a camera or screen capture.
Create a local video stream based on the camera video source:
val cameraVideoSource = CameraVideoSource
val localVideoStream = LocalVideoStream(cameraVideoSource)
Calls
Voximplant Android SDK v3 has a separate classes that represent a call and a conference. It provides a clearer visibility of the SDK functionality for calls and conferences.
Notable changes:
Separated call and conference settings to start a call or a conference
Separated call and conferences events
Endpoints are now available only for the conferences. Information about another call participant as well as its events is now available via the
CallAPI.Local video stream management before a call/conference — it is now possible to create and render a local video stream before a call or a conference starts. New API provide more flexible way to manage the video stream sources such as camera, custom, or screen sharing video source.
Native webrtc libraries are loaded on the calls module initialization
Before you start using the calls module API, you should initialize the module via the VICalls.initialize API.
Start an outgoing audio call
The VideoFlags API that represents the video settings for a call is replaced with CallSettings.receiveVideo and CallSettings.localVideoStream properties. Video is disabled by default.
From v2:
import com.voximplant.sdk.call.CallException
import com.voximplant.sdk.call.CallSettings
import com.voximplant.sdk.call.ICall
import com.voximplant.sdk.call.VideoFlags
// 1. Prepare callSettings
val callSettings = CallSettings()
callSettings.videoFlags = VideoFlags(false, false)
// 2. Get an ICall instance
val call: ICall? = client.call("Number to call", callSettings)
// 3. Start the call
try {
call!!.start()
} catch (callException: CallException) {
// handle error
}
To v3:
import com.voximplant.android.sdk.calls.Call
import com.voximplant.android.sdk.calls.CallException
import com.voximplant.android.sdk.calls.CallSettings
import com.voximplant.android.sdk.calls.VICalls
// 1. Create a Call instance
val call: Call? = VICalls.createCall(
number = "number to call",
callSettings = CallSettings(),
)
// 2. Start the call
try {
call!!.start()
} catch (callException: CallException) {
// handle error
}
Start an outgoing video call
The VideoFlags API that represents the video settings for a call is replaced with CallSettings.receiveVideo and CallSettings.localVideoStream properties. Video is disabled by default.
From v2:
import com.voximplant.sdk.call.CallException
import com.voximplant.sdk.call.CallSettings
import com.voximplant.sdk.call.ICall
import com.voximplant.sdk.call.VideoFlags
// 1. Prepare callSettings
val callSettings = CallSettings()
callSettings.videoFlags = VideoFlags(false, false)
// 2. Get an ICall instance
val call: ICall? = client.call("Number to call", callSettings)
// 3. Start the call
try {
call!!.start()
} catch (callException: CallException) {
// handle error
}
To v3:
import com.voximplant.android.sdk.calls.Call
import com.voximplant.android.sdk.calls.CallException
import com.voximplant.android.sdk.calls.CallSettings
import com.voximplant.android.sdk.calls.VICalls
// 1. Create a Call instance
val call: Call? = VICalls.createCall(
number = "number to call",
callSettings = CallSettings(),
)
// 2. Start the call
try {
call!!.start()
} catch (callException: CallException) {
// handle error
}
Start an outgoing video call
To start an outgoing video call, it is required to create a local video stream first.
Call cannot be created with a local video stream with screen capture video source. However, it is possible to start sending the screen sharing after the call is connected.
From v2:
import com.voximplant.sdk.call.CallException
import com.voximplant.sdk.call.CallSettings
import com.voximplant.sdk.call.ICall
import com.voximplant.sdk.call.VideoFlags
// 1. Prepare callSettings
val callSettings = CallSettings()
callSettings.videoFlags = VideoFlags(true, true)
// 2. Get an ICall instance
val call: ICall? = client.call(user, callSettings)
// 3. Start the call
try {
call!!.start()
} catch (callException: CallException) {
// handle error
}
To v3:
import com.voximplant.android.sdk.calls.Call
import com.voximplant.android.sdk.calls.CallException
import com.voximplant.android.sdk.calls.CallSettings
import com.voximplant.android.sdk.calls.LocalVideoStream
import com.voximplant.android.sdk.calls.VICalls
import com.voximplant.android.sdk.calls.video.CameraVideoSource
// 1. Create a local video stream
val cameraVideoSource = CameraVideoSource
val localVideoStream = LocalVideoStream(cameraVideoSource)
// 2. Prepare the call settings
val callSettings = CallSettings()
callSettings.localVideoStream = localVideoStream
callSettings.receiveVideo = true
// 3. Create a call
val call: Call? = VICalls.createCall(
number = "number_to_call",
callSettings = callSettings,
)
// 4. Start the call
try {
call!!.start()
} catch (callException: CallException) {
// handle error
}
Handle incoming calls
To receive the incoming calls it is required to subscribe to IncomingCallListener via the VICalls.setIncomingCallListener API.
From v2:
import com.voximplant.android.sdk.calls.CallException
import com.voximplant.android.sdk.calls.CallSettings
import com.voximplant.sdk.client.IClient
import com.voximplant.sdk.client.IClientIncomingCallListener
class SomeClass : IClientIncomingCallListener {
private lateinit var client: IClient
fun subscribeForIncomingCalls() {
client.setClientLoginListener(this)
}
override fun onIncomingCall(
call: ICall,
video: Boolean,
headers: Map<String?, String?>?
) {
try {
call.answer(CallSettings())
} catch (callException: CallException) {
// handle error
}
}
}
To v3:
import com.voximplant.android.sdk.calls.Call
import com.voximplant.android.sdk.calls.CallException
import com.voximplant.android.sdk.calls.CallSettings
import com.voximplant.android.sdk.calls.IncomingCallListener
class SomeClass {
val incomingCallListener = object : IncomingCallListener {
override fun onIncomingCall(
call: Call,
hasIncomingVideo: Boolean,
headers: Map<String, String>?,
) {
try {
call.answer(CallSettings())
} catch (callException: CallException) {
// handle error
}
}
}
init {
VICalls.setIncomingCallListener(incomingCallListener)
}
}
Handle call events changes
Call’s and conference’s events are separated into different interface listeners: CallListener and ConferenceListener.
The following call events are renamed:
ICalListener.onCallRinging → CallListener.onStartRinging — the event is triggered when the Call.ring method is called on the scenario side
ICallListener.onCallAudioStarted → CallListener.onStopRinging — the event is triggered when the call media starts soon and the progress tones played on the application side should be stopped.
Remote participant in a call is no more represented via the Endpoint interface. To handle the events related to a remote video stream in a call, it is required to subscribe to the following events:
CallListener.onRemoteVideoStreamAdded — the event is triggered when a remote participant has started to send a video in a call
CallListener.onRemoteVideoStreamRemoved — the event is triggered when a remote participant has stopped sending a video in a call
CallListener.onCallDisconnected now has the disconnectReason parameter that specifies the reason the call has ended.
Conferences
Join a conference with video
From v2:
import com.voximplant.sdk.call.CallException
import com.voximplant.sdk.call.CallSettings
import com.voximplant.sdk.call.ICall
import com.voximplant.sdk.call.VideoFlags
// 1. Prepare callSettings
val callSettings = CallSettings()
callSettings.videoFlags = VideoFlags(true, true)
// 2. Get a ICall instance
val call: ICall? = call.callConference(conferenceName, callSettings)
// 3. Start the call
try {
call!!.start()
} catch (callException: CallException) {
// handle error
}
To v3:
import com.voximplant.android.sdk.calls.CallException
import com.voximplant.android.sdk.calls.Conference
import com.voximplant.android.sdk.calls.ConferenceSettings
import com.voximplant.android.sdk.calls.LocalVideoStream
import com.voximplant.android.sdk.calls.VICalls
import com.voximplant.android.sdk.calls.video.CameraVideoSource
// 1. Create a local video stream
val cameraVideoSource = CameraVideoSource
val localVideoStream = LocalVideoStream(cameraVideoSource)
conferenceSettings.receiveVideo = true
// 2. Prepare the conference settings
val conferenceSettings = ConferenceSettings()
conferenceSettings.localVideoStream = localVideoStream
conferenceSettings.receiveVideo = true
// 3. Create a conference
val conference: Conference? = VICalls.createConference(
number = "destination",
conferenceSettings = conferenceSettings,
)
// 4. Join the conference
try {
conference!!.join()
} catch (callException: CallException) {
// handle error
}
Handle conference events changes
Call’s and conference’s events are separated into different interface listeners: CallListener and ConferenceListener.
New conference events:
ConferenceListener.onLocalVoiceActivityChanged — the event is triggered when a voice activity is detected on the current user in a conference
EndpointListener.onEndpointMuteStateChanged — the event is triggered when a remote conference participant mutes or unmutes his audio via the Conference.muteAudio API
Other changes:
ConferenceListener.onEndpointAdded is not triggered with an endpoint which id is equal to the conference id
ConferenceListener.onConferenceDisconnected now has the
disconnectReasonparameter that specifies the reason the conference has endedConferenceListener.onEndpointRemoved event is moved from EndpointListener to the ConferenceListener
ConferenceListener.onEndpointAdded event is triggered when a conference participant starts the screen sharing
Composable function to render a video stream
Voximplant Android SDK v3 has a new com.voximplant:android-sdk-renderer-compose module that provides a composable function VideoRenderer to render a local or remote video stream in the application built with Jetpack Compose.
import com.voximplant.android.sdk.calls.RenderScaleType
import com.voximplant.android.sdk.renderer.compose.VideoRenderer
@Composable
fun VideoRendererExample() {
VideoRenderer(
modifier = Modifier.wrapContentSize(),
videoStream = videoStream,
renderScaleType = RenderScaleType.Balanced,
)
}
Capture SDK logs
Voximplant Android SDK v3 provides a more flexible interface to capture SDK logs with extended information such as a log timestamp, a thread id, and a throwable for exceptions.
Log collection configuration is done for all modules at once and is recommended on the SDK initialization.
From v2:
import com.voximplant.sdk.Voximplant
import com.voximplant.sdk.client.ILogListener
import com.voximplant.sdk.client.LogLevel
class SDKLogger() : ILogListener {
init {
Voximplant.setLogListener(this)
}
override fun onLogMessage(level: LogLevel, log: String) {
val message = "$level $message"
// save the log message to a file or send it to a remote service
}
}
To v3:
import com.voximplant.android.sdk.core.VICore
import com.voximplant.android.sdk.core.logging.LogLevel
import com.voximplant.android.sdk.core.logging.Logger
VICore.logging.logger = object : Logger {
override fun log(
level: LogLevel,
threadId: Int,
time: Date,
message: String,
throwable: Throwable?,
) {
val message = "$time $threadId $message $throwable"
// save the log message to a file or send it to a remote service
}
}
VICore.logging.level = LogLevel.Info
VICore.logging.enableLogcat = false