Rate this page:

Android SDK 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

Copy URL

SDK Initialization

Copy URL

Core module initialization

The Core module should be initialized first via VICore.initialize. It is recommended to perform this initialization at application startup, though it can be done at any time prior to using the SDK.

In SDK v3, the executor used for receiving SDK events is created automatically as a single-thread executor. It can be manually configured via the VICore.callbackExecutor property after initialization.

From v2:

val client = Voximplant.getClientInstance(
    /* executor = */ Executors.newSingleThreadExecutor(),
    /* context = */ applicationContext,
    /* clientConfig = */ ClientConfig(),
)

To v3:

// Optional: Configure a custom callback executor
VICore.callbackExecutor = Executors.newSingleThreadExecutor()
VICore.initialize(applicationContext)

Initialization state can be verified using VICore.isInitialized.

Calls module initialization

The Calls module requires the Core module to be initialized first using the VICalls.initialize, but its initialization does not need to occur immediately following VICore.initialize.

VICalls.initialize()

Initialization state can be verified using VICalls.isInitialized.

Connect and authenticate the Voximplant users

Copy URL

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

Copy URL

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:

  1. a video renderer is added to the local video stream
  2. a local video stream is attached to an active call or a conference
Please note

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:

  1. a local video stream is not rendered to any renderer
  2. a local video stream is not sent in any call or conference
Warning

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

Copy URL

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 Call API.

  • 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

Please note

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

To start an outgoing video call, it is required to create a local video stream first.

Warning

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:

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.onCallDisconnected now has the disconnectReason parameter that specifies the reason the call has ended.

Conferences

Copy URL

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)

// 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:

Other changes:

Composable function to render a video stream

Copy URL

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,
    )
}

Push notification settings

Copy URL

In Android SDK v2, push notification settings are configured inside ClientConfig when creating the client instance. In Android SDK v3, these settings are decoupled and passed directly to their corresponding API methods.

Mapping Changes:

From v2:

val config = ClientConfig()
config.packageName = packageName
config.useHmsForPushNotifications = true

val client = Voximplant.getClientInstance(
    executor,
    applicationContext,
    config,
)

To v3:

Client.connect(
    options = ConnectOptions(node = Node).apply {
        services = MobileServices.Google
    },
    callback = connectionCallback,
)
Client.registerForPushNotifications(
    pushConfig = PushConfig(pushToken, applicationContext.packageName),
    callback = pushCallback,
)

Capture SDK logs

Copy URL

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
Rate this page: