-
Notifications
You must be signed in to change notification settings - Fork 17
Infobip RTC calls and UI
Find out more about Voice/Video Calls and Infobip RTC features in the Infobip docs.
- Intro
- Requirements
- Permissions
- Migration guide
- Quick start guide
- Customizing the calls UI
- Changing localization
- Errors mapping
- Firebase Messaging Service delegation
InfobipRtcUi is an easy-to-use library that allows you to connect to the Infobip RTC by building a library.
This guide assumes that you have already set up your account and your mobile app profile with Mobile Push Notifications and the Infobip RTC.
InfobipRtcUi takes care of everything: the registration of your device to receive and trigger calls and handling of the calls themselves. It also offers you a powerful user interface with every feature your customer may need:
- the ability to capture video through both, front and back camera,
- an option to mute and use the speaker,
- the ability to capture and share a screen of a mobile device,
- an option to minimize the call UI in a picture-on-picture mode, and more.
InfobipRtcUi also allows you to take control at any step of the flow, if you wish or need so. How you handle the calls is up to you. You can become a delegate for the FirebaseMessagingService or use your own custom user interface.
- Android Studio
- Supported API Levels: 21 (Android 5.0 - Lollipop) - 34 (Android 14.0 - Upside Down Cake)
- AndroidX
- InfobipRtcUi library source and target compatibility is set to Java 8.
- Infobip account
InfobipRtcUi library declares the following dangerous
permissions:
Runtime check and request for dangerous permissions is handled by library UI components.
Below, is the list of normal
permissions declared in the library:
WAKE_LOCK
FOREGROUND_SERVICE
VIBRATE
-
USE_FULL_SCREEN_INTENT
Starting from Android 14, Google can revoke the permission for your application. As a result, you may miss an incoming call when a phone is locked. Read more about Android 14 Full-screen intent notifications and how to avoid missed calls.
FOREGROUND_SERVICE
FOREGROUND_SERVICE_PHONE_CALL
MANAGE_OWN_CALLS
FOREGROUND_SERVICE_MEDIA_PROJECTION
Following the major release of Infobip WebRTC SDK 2.0, content and setup exclusive of Infobip WebRTC SDK 1.x will be deprecated on 31/10/2023. If you were using InfobipRtcUi previously (version 9.X or older), please update to the latest version and read carefully the Quick Start Guide below. You will need a new setup and change function to enable calls. Things to consider:
- Portal UI for handling WebRTC has been replaced with REST API calls.
- The previous WebRTC application you used must be replaced with two new separate models: CPaaS X Application and WebRTC Push Configuration.
- The WebRTC
applicationId
you used before to enable calls in mobile needs to be replaced with a new WebRTC PushconfigrationId
. - If you want to use InfobipRtcUi and InAppChat together (meaning, you want to receive calls from Infobip Conversation's agents), you need to use the new
withInAppChatCalls()
builder function, instead of the previousenableInAppCalls()
.
You can find complete list of changes in migration guide.
-
Include the
InfobipRtcUi
dependencyimplementation ('com.infobip:infobip-rtc-ui:12.3.0') { transitive = true }
-
Set up the Mobile Messaging SDK.
Obtain a Firebase configuration file (
google-services.json
) as described in documentation and move your config file into the module (app-level) root directory of your app. -
Create your CPaaS X Application and WebRTC Push Configuration resource as described in a guide. You can create CPaaS X Application in our REST API. Then create WebRTC Push Configuration with the REST API with
applicationId
of CPaaS X Application from previous request and your Google Private Key JSON file. -
Provide WebRTC Push Configuration id obtained in the step 3 to InfobipRtcUi. There are 2 options how to do it:
- Introduce new string variable in resources file
values/strings.xml
.<resources> <string name="infobip_webrtc_configuration_id">WEBRTC CONFIGURATION ID</string> ... </resources>
- Provides it in runtime using builder function showed in next step.
- Introduce new string variable in resources file
-
Build an InfobipRtcUi SDK:
class App: android.app.Application() { override fun onCreate() { super.onCreate() InfobipRtcUi.Builder(this) .withConfigurationId("WebRTC Configuration ID") //optional step, builder provided ID has precedent over ID provided in resources .withInAppChatCalls() //optional step, enables InAppChat calls .build() } }
expand to see Java code
public class Application extends android.app.Application { @Override public void onCreate() { super.onCreate(); new InfobipRtcUi.Builder(this) .withConfigurationId("WebRTC Configuration ID") //optional step, builder provided ID has precedent over ID provided in resources .withInAppChatCalls() //optional step, enables InAppChat calls .build(); } }
-
Register for incoming calls based on your use case:
-
If you use InfobipRtcUi together with InAppChat, there is prepared function you need use:
InfobipRtcUi.getInstance(context).enableInAppChatCalls( successListener = {}, errorListener = { throwable -> } )
expand to see Java code
InfobipRtcUi.getInstance(context).enableInAppChatCalls( () -> {}, (throwable) -> {} );
There is builder function
withInAppChatCalls(successListener, errorListener)
which also registers you for incoming calls, where you don't need to useInfobipRtcUi
instance. -
If you plan to use InfobipRtcUi where you can define call identity and listen type on your own, there is prepared function you can use:
InfobipRtcUi.getInstance(context).enableCalls( identity = "customIdentity", listenType = ListenType.PUSH, successListener = {}, errorListener = { throwable -> } )
expand to see Java code
InfobipRtcUi.getInstance(context).enableCalls( "customIdentity", ListenType.PUSH, () -> {}, (throwable) -> {} );
There is builder function
withCalls(identity, listenType, successListener, errorListener)
which also registers you for incoming calls, where you don't need to useInfobipRtcUi
instance. -
If you don't want to care about identity, you can left responsibility for picking unique identity on InfobipRtcUi. InfobipRtcUi will use per device, per installation unique
pushRegistrationId
as identity. There is also prepared function for such use case you can use:InfobipRtcUi.getInstance(context).enableCalls( successListener = {}, errorListener = { throwable -> } )
expand to see Java code
InfobipRtcUi.getInstance(context).enableCalls( () -> {}, (throwable) -> {} );
There is builder function
withCalls(successListener, errorListener)
which also registers you for incoming calls, where you don't need to useInfobipRtcUi
instance.
-
The UI for interacting with calls is important. For this reason, we offer several options of customization:
- Use our default UI that will work out of the box.
- You can override colors, icons and a message shown on the incoming call screen using InfobipRtcUi theme customization.
- You can customize buttons that will be available during a call.
- You can use your own
Activity
to handle a call.
To use InfobipRtcUi theme customization you have to define your own custom theme. There are two ways to do that:
-
Define style in application's
styles.xml
with nameInfobipRtcUi
.<style name="InfobipRtcUi"> <!-- color of text and elements in foreground --> <item name="rtc_ui_color_foreground">#ffffff</item> <!-- background color of UI elements and layouts --> <item name="rtc_ui_color_background">#242424</item> <!-- color of less prominent texts --> <item name="rtc_ui_color_text_secondary">#7AFFFFFF</item> <!-- background color of toolbar in video call --> <item name="rtc_ui_color_overlay_background">#80242424</item> <!-- background color of accept call action button, icon use rtc_ui_color_actions_icon --> <item name="rtc_ui_color_accept">#29B899</item> <!-- background color of hangup call action button, icon use rtc_ui_color_actions_icon --> <item name="rtc_ui_color_hangup">#C84714</item> <!-- background color of alerts during a call --> <item name="rtc_ui_color_alert_background">#99050708</item> <!-- color of text in alerts during a call --> <item name="rtc_ui_color_alert_text">#ffffff</item> <!-- muted microphone alert icon during a call --> <item name="rtc_ui_icon_alert_mic_off">@drawable/ic_mic_off</item> <!-- weak connection alert icon during a call --> <item name="rtc_ui_icon_alert_weak_connection">@drawable/ic_alert_triangle</item> <!-- call reconnecting alert icon during a call --> <item name="rtc_ui_icon_alert_reconnecting">@drawable/ic_alert_triangle</item> <!-- background color of call action buttons excluding hangup and accept call --> <item name="rtc_ui_color_actions_background">#15FFFFFF</item> <!-- background color of call action buttons in active state --> <item name="rtc_ui_color_actions_background_checked">#ffffff</item> <!-- color of call action icon --> <item name="rtc_ui_color_actions_icon">#ffffff</item> <!-- color of call action icon in active state --> <item name="rtc_ui_color_actions_icon_checked">#B2242424</item> <!-- background color of secondary call action buttons --> <item name="rtc_ui_color_actions_row_background">#242424</item> <!-- color of secondary call action icon --> <item name="rtc_ui_color_actions_row_icon">#ffffff</item> <!-- color of secondary call action label --> <item name="rtc_ui_color_actions_row_label">#ffffff</item> <!-- color of actions bottom sheet dragging "pill" --> <item name="rtc_ui_color_sheet_pill">#15FFFFFF</item> <!-- color of actions bottom sheet divider line --> <item name="rtc_ui_color_actions_divider">#15FFFFFF</item> <!-- background color of actions bottom sheet --> <item name="rtc_ui_color_sheet_background">#242424</item> <!-- drawable references for action icons and call images --> <item name="rtc_ui_icon_mic">@drawable/ic_mic</item> <item name="rtc_ui_icon_micOff">@drawable/ic_mic_off</item> <item name="rtc_ui_icon_screenShare">@drawable/ic_screen_share</item> <item name="rtc_ui_icon_screenShareOff">@drawable/ic_screen_share_off</item> <item name="rtc_ui_icon_video">@drawable/ic_video</item> <item name="rtc_ui_icon_videoOff">@drawable/ic_video_off</item> <item name="rtc_ui_icon_speaker">@drawable/ic_speaker</item> <item name="rtc_ui_icon_speakerOff">@drawable/ic_speaker_off</item> <item name="rtc_ui_icon_flipCamera">@drawable/ic_flip_camera</item> <item name="rtc_ui_icon_collapse">@drawable/ic_collapse</item> <item name="rtc_ui_icon_accept">@drawable/ic_calls_30</item> <item name="rtc_ui_icon_decline">@drawable/ic_clear_large</item> <item name="rtc_ui_icon_endCall">@drawable/ic_endcall</item> <item name="rtc_ui_icon_avatar">@drawable/ic_user_grayscale</item> <item name="rtc_ui_icon_caller">@drawable/ic_calls_30</item> <!-- incoming call screen customizations --> <item name="rtc_ui_incoming_call_headline">@string/incoming_call_headline</item> <item name="rtc_ui_incoming_call_headline_appearance">@style/TextAppearance.AppCompat.Title</item> <item name="rtc_ui_incoming_call_headline_text_color">#ffffff</item> <item name="rtc_ui_incoming_call_headline_background">@null</item> <item name="rtc_ui_incoming_call_message">@string/incoming_call_message</item> <item name="rtc_ui_incoming_call_message_appearance">@style/TextAppearance.AppCompat.Title</item> <item name="rtc_ui_incoming_call_message_text_color">#ffffff</item> <item name="rtc_ui_incoming_call_message_background">@null</item> <item name="rtc_ui_incoming_call_caller_name">@null</item> <item name="rtc_ui_incoming_call_caller_name_appearance">@style/TextAppearance.AppCompat.Body</item> <item name="rtc_ui_incoming_call_caller_icon_visible">true</item> <!-- in call screen customizations --> <item name="rtc_ui_in_call_caller_name">@string/in_call_caller_name</item> </style>
-
In code, you can use
InfobipRtcUi.setTheme(InfobipRtcUiTheme)
, which provides same customization as the xml approach.InfobipRtcUi.getInstance(context).setTheme( InfobipRtcUiTheme( incomingCallScreenStyle = IncomingCallScreenStyle( headlineText = "your custom headline", messageText = "your custom message" ), inCallScreenStyle = InCallScreenStyle( callerName = "your custom caller name" ), colors = Colors( //define your custom colors ), icons = Icons( //define your custom icons ) ) )
expand to see Java code
InfobipRtcUi.getInstance(context).setTheme( new InfobipRtcUiTheme( new IncomingCallScreenStyle( //define your custom style ), new InCallScreenStyle( //define your custom style ), new Colors( //define your custom colors ), new Icons( //define your custom icons ) ) );
Final value for every InfobipRtcUiTheme
theme attribute is resolved from multiple source-by-source priority. The source with the highest priority defines a final attribute value. If source does not
define an attribute value, there is fallback to the source with lower priority.
Sources by priority:
-
InfobipRtcUiTheme
theme provided in runtime using this function. -
InfobipRtcUi
style provided in xml. - Default
InfobipRtcUi
style defined by InfobipRtcUi library
Final value for every theme attribute is evaluated separately. It means you can define InfobipRtcUiTheme.incomingCallScreenStyle
in runtime, colors in xml and skip icons. Library will
use InfobipRtcUiTheme.incomingCallScreenStyle
you defined in runtime, colors you defined in xml and default icons provided by library itself.
To customize order (or presence) of call action buttons use InfobipRtcUi.setInCallButtons(buttons: List<InCallButton>)
. First 3 buttons are visible in main area (half-expanded state of bottom sheet) with HangUp
button, rest is visible in full expanded state (secondary buttons). There are these types of buttons available:
sealed class InCallButton(
...
) {
object HangUp: InCallButton() // always at the first place, non-editable
data class Mute(override var onClick: () -> Unit = {}): InCallButton()
data class Speaker(override var onClick: () -> Unit = {}): InCallButton()
data class FlipCam(override var onClick: () -> Unit = {}): InCallButton() // only visible when local video is active
data class ScreenShare(override var onClick: () -> Unit = {}): InCallButton()
data class Video(override var onClick: () -> Unit = {}): InCallButton()
data class Custom(
@StringRes override val labelRes: Int,
@DrawableRes override val iconRes: Int,
@DrawableRes override var checkedIconRes: Int? = null,
override var onClick: () -> Unit,
val isChecked: (() -> Boolean)? = null,
val isEnabled: (() -> Boolean)? = null
): InCallButton()
}
InfobipRtcUi.getInstance(context).setInCallButtons(
listOf(
InCallButton.Mute(onClick = {}),
InCallButton.Video(onClick = {})
)
)
expand to see Java code
import kotlin.Unit;
InCallButton mute = new InCallButton.Mute(() -> {
return Unit.INSTANCE;
});
InCallButton video = new InCallButton.Mute(() -> {
return Unit.INSTANCE;
});
List<InCallButton> buttons = new ArrayList<>();
buttons.add(mute);
buttons.add(video);
InfobipRtcUi.getInstance(this).setInCallButtons(buttons);
All buttons, except Custom
, have a defined behaviour (isEnabled
and isChecked
values) and icons. You can only enrich the behaviour of the onClick
event. Custom buttons are customizable via constructor-provided values. Labels are visible only for buttons in secondary area.
Use a completely new user interface of your own. You can define own Activity
to handle a call.
InfobipRtcUi.Builder(context)
.withCustomActivity(YourActivity.class)
.build()
expand to see Java code
new InfobipRtcUi.Builder(context)
.withCustomActivity(YourActivity.class)
.build();
An incoming call push notification will be handled by the
InfobipRtcUi
SDK. To build your own UI, visit the InfobipRTC wiki page for all options.
Whole UI provided by InfobipRtcUi is by default localized for the English locale, but it can be changed by providing your own locale. The call's UI must be recreated to apply a new locale.
InfobipRtcUi.getInstance(context).setLanguage(Locale("es", "ES"))
expand to see Java code
InfobipRtcUi.getInstance(context).setLanguage(new Locale("es", "ES"))
InfobipRtcUi defines default localised human-readable messages for certain errors, but you can replace default messages with your own messages. Implement RtcUiCallErrorMapper
interface and
provide the implementation into InfobipRtcUi library using InfobipRtcUi.setErrorMapper(errorMapper: RtcUiCallErrorMapper)
function.
RtcUiCallErrorMapper
interface contains single function you must implement fun getMessageForError(error: RtcUiError): String?
. Function provides you RtcUiError
instance with all error
specific data. Based on the error you should return message to be show to the user. If returned message is null
, empty or blank, it is not shown to the user. This way you can ignore particular
errors.
You can find all possible errors defined by InfobipRtcUi library in RtcUiError.Companion
class. There are also general WebRTC errors defined in Infobip WebRTC documentation.
import com.infobip.webrtc.sdk.api.model.ErrorCode
import com.infobip.webrtc.ui.model.RtcUiError
InfobipRtcUi.getInstance(context).setErrorMapper(
object : RtcUiCallErrorMapper {
override fun getMessageForError(error: RtcUiError): String? {
return when(error.name){
RtcUiError.MISSING_POST_NOTIFICATIONS_PERMISSION.name -> "Missing permission"
...
ErrorCode.NORMAL_HANGUP.name -> null //do not show message when it is normal call hangup
else -> error.description ?: error.name
}
}
}
)
expand to see Java code
InfobipRtcUi.getInstance(context).setErrorMapper(error -> {
String errorMsg = error.getDescription();
if (error.getName().equals(RtcUiError.MISSING_POST_NOTIFICATIONS_PERMISSION.getName())) {
errorMsg = "Missing permission";
}
if (error.getName().equals(ErrorCode.NORMAL_HANGUP.getName())) {
errorMsg = null;
}
return errorMsg;
});
Let's assume that you use an FCM to process push-from-native backend and you don't want to migrate all code to use Infobip. There still can be a use case where you want to send notifications directly to Firebase. In that case, you have your own service that extends FirebaseMessagingService
. Infobip SDK also extends the same service and if your service is registered in the manifest, Infobip's service won't be used for message handling (won't be able to receive calls).
We provide InfobipRtcUiFirebaseService
to solve this issue. You have 2 options:
- Inherit from the service - it is easier, faster and more error-prone way.
- Use service's static functions - it provides you full control over Firebase's
RemoteMessage
handling, but it is your responsibility to integrate it in proper way to make calls working.
Inherit from our InfobipRtcUiFirebaseService
and put your Firebase implementation into delegates methods you must override. Message delegation to Mobile Messaging SDK and token handling is covered. Don't forget to register your service in Manifest.xml
.
import com.google.firebase.messaging.RemoteMessage
import com.infobip.webrtc.ui.service.InfobipRtcUiFirebaseService
class YourFirebaseService : InfobipRtcUiFirebaseService() {
override fun onMessageReceivedDelegate(message: RemoteMessage) {
// process non-Infobip notifications here
// TODO your code
}
override fun onNewTokenDelegate(token: String) {
// process Firebase token here
// TODO your code
}
}
expand to see Java code
import com.google.firebase.messaging.RemoteMessage;
import com.infobip.webrtc.ui.service.InfobipRtcUiFirebaseService;
class YourFirebaseService extends InfobipRtcUiFirebaseService {
@Override
void onMessageReceivedDelegate(RemoteMessage message) {
// process non-Infobip notifications here
// TODO your code
}
@Override
void onNewTokenDelegate(String token) {
// process Firebase token here
// TODO your code
}
}
Declare service inside your AndroidManifest.xml
.
<service
android:name=".YourFirebaseService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
There are cases when you need full control over message handling and cannot inherit from our service. Both Mobile Messaging SDK and InfobipRtcUi SDK provides public functions you are supposed to use. It is your responsibility to call them in correct places to achieve expected behaviour. Without proper integration:
- Mobile Messaging SDK will not process push notifications
- InfobipRtcUi SDK will not receive incoming calls
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
class YourFirebaseService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
if (MobileMessagingFirebaseService.onMessageReceived(this, remoteMessage)) {
return
}
if (InfobipRtcUiFirebaseService.onMessageReceived(this, remoteMessage)) {
return
}
// process non-Infobip notifications here
// TODO your code
}
override fun onNewToken(token: String) {
super.onNewToken(token)
MobileMessagingFirebaseService.onNewToken(this, token)
InfobipRtcUiFirebaseService.onNewToken(this, token)
// process Firebase token here
// TODO your code
}
}
expand to see Java code
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
class YourFirebaseService extends FirebaseMessagingService {
@Override
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
if (MobileMessagingFirebaseService.onMessageReceived(this, remoteMessage)) {
//remoteMessage belongs to Mobile Messaging SDK and was handled
return;
}
if (InfobipRtcUiFirebaseService.onMessageReceived(this, remoteMessage)) {
//remoteMessage belongs to InfobipRtcUi SDK and was handled
return;
}
// process non-Infobip notifications here
// TODO your code
}
@Override
public void onNewToken(@NonNull String token) {
super.onNewToken(s);
MobileMessagingFirebaseService.onNewToken(this, token);
InfobipRtcUiFirebaseService.onNewToken(this, token);
// process Firebase token here
// TODO your code
}
}
Declare service inside your AndroidManifest.xml
.
<service
android:name=".YourFirebaseService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
If you have any questions or suggestions, feel free to send an email to [email protected] or create an issue.
- Library events
- Server errors
- Users and installations
- Messages and notifications management
- Inbox
Geofencing API- DEPRECATED- Android Manifest components
- Privacy settings
- In-app chat
- Infobip RTC calls and UI
- Backup rules