Android SDK
About
WebRTC-based real-time communication library for Android, providing multi-party video conferencing, 1-to-1 calls, screen sharing, mobile co-browse, AR pointer and Picture-in-Picture support.
You can find the latest examples on how to use our Android SDK at our Github Account.
Features
- Multi-party video conferencing with reactive state management (call layouts for up to 8 participants)
- Call modes: join with audio only, video only, or both, and open the missing camera or microphone during the call
- Mobile co-browse: share this app's screen with an agent, standalone or from a video call, with masking of sensitive views and two-way annotations
- AR pointer: the agent points and draws on the customer's camera or screen share, and the customer can draw back
- Automatic recovery of the call's streams and MQTT connection after a network drop
- Multiple concurrent streams (camera + screen share)
- Picture-in-Picture (PiP) mode support
- Screen sharing via MediaProjection API
- Reactive state management with Kotlin Flows (StateFlow/SharedFlow)
- Thread-safe stream repository pattern
- Camera switching without stream interruption
- Audio/video track toggling (mute/unmute)
- Pre-built UI components for quick integration (fill/fit and pinch-to-zoom on remote videos, placeholder tiles for participants without video)
- Configurable remote error reporting (Sentry), including your own DSN
- Modern Kotlin coroutines API
Requirements
- Android SDK 23+ (Android 6.0 Marshmallow)
- Target SDK 36
- Kotlin 2.1.20+
- Java 17
The Auvious Android SDK is built using Java 17 and requires your Android project to be configured to use Java 17 for compilation. Ensure your module-level build.gradle (or build.gradle.kts) file includes:
kotlin {
jvmToolchain(17)
}
and that your app's minSdk is at least 23:
android {
defaultConfig {
minSdk 23
}
}
Setup Instructions
1. Add Maven Repository
Add the Auvious Maven repository to your build.gradle or settings.gradle:
repositories {
mavenCentral()
// Auvious SDK Repository
maven {
url = uri("https://nexus.auvious.com/repository/maven-releases")
}
}
2. Add SDK Dependency
Add the Auvious SDK dependency to your app's build.gradle:
dependencies {
// Auvious SDK
implementation("com.auvious.android:sdk:1.3.1")
}
3. Required Permissions
Add these permissions to your AndroidManifest.xml:
<!-- Camera and microphone permissions -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!-- Internet and network state -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Optional: For screen sharing and co-browse support -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<!-- Optional: shows the ongoing screen-sharing notification. Request it at runtime on Android 13+;
without it the share still works, but its notification doesn't appear in the notification shade. -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
4. Picture-in-Picture Support
If you want to support PiP mode, add this to your Activity in AndroidManifest.xml:
<activity
android:name=".YourConferenceActivity"
android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:resizeableActivity="true"
android:launchMode="singleTask" />
To disable screen sharing and avoid the foreground service permissions, set screenShareAvailability to "false" in your conference options, and you can remove the foreground service permissions from your manifest:
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION"
tools:node="remove"/>
5. Google Play Console Requirements
For apps targeting Android 14+ / API 34+:
If your app uses screen sharing functionality, you must complete the Foreground Service declaration in Google Play Console before publishing:
- Navigate to: Play Console → Your App → App content → Foreground service
- Declare the
mediaProjectionforeground service type - Provide:
- Description of screen sharing functionality in your app
- Video demonstration showing:
- User action that initiates screen sharing
- Screen sharing in progress with notification visible
- User action to stop screen sharing
- Justification explaining the user benefit
Important: Google requires video evidence showing the feature is user-initiated. Failure to complete this declaration will result in app rejection during review.
For more details, see Google's foreground service requirements.
Quick Start
Pre-Built UI
Use AuviousSimpleConferenceActivity for a ready-made conference UI:
private fun startSimpleConferenceActivity() {
val callOptions = AuviousSimpleConferenceOptions(
clientId = "customer",
baseEndpoint = "https://auvious.video",
mqttEndpoint = "wss://auvious.video/ws",
params = mapOf(
"ticket" to "your-conference-ticket",
"grant_type" to "password",
AuviousSimpleConferenceOptions.callMode to AuviousCallMode.AUDIO_VIDEO.value,
AuviousSimpleConferenceOptions.speakerOption to "true",
AuviousSimpleConferenceOptions.microphoneAvailable to "true",
AuviousSimpleConferenceOptions.cameraAvailable to "true",
AuviousSimpleConferenceOptions.speakerAvailable to "true"
)
)
val intent = AuviousSimpleConferenceActivity.getIntent(this, callOptions)
startActivity(intent)
}
Multi-Stream Support
The SDK supports publishing multiple streams concurrently:
// Publish camera stream
val cameraStreamId = conferenceSDK.publishStream(
sources = listOf(
StreamSource.Camera(CameraFacing.FRONT, deviceId = "0", resolution = Resolution.HD),
StreamSource.Microphone()
)
).getOrThrow()
// Publish screen share (concurrent with camera)
val screenStreamId = conferenceSDK.publishStream(
sources = listOf(
StreamSource.Screen(resultCode = resultCode, permissionData = data)
)
).getOrThrow()
// Both streams coexist independently
// Remote participants see both your camera and screen
Stream Control
Mute/Unmute Audio
// Mute microphone
streamRepository.toggleAudioTrack(streamId = cameraStreamId, enabled = false).getOrThrow()
// Unmute microphone
streamRepository.toggleAudioTrack(streamId = cameraStreamId, enabled = true).getOrThrow()
Disable/Enable Video
// Disable camera
streamRepository.toggleVideoTrack(streamId = cameraStreamId, enabled = false).getOrThrow()
// Enable camera
streamRepository.toggleVideoTrack(streamId = cameraStreamId, enabled = true).getOrThrow()
Switch Camera
// Switch between front and back camera (stream ID stays the same)
streamRepository.switchCamera(streamId = cameraStreamId).getOrThrow()
Observing Streams
Observe Local Streams
lifecycleScope.launch {
streamRepository.publishedStreams.collect { localStreams ->
localStreams.forEach { stream ->
when (stream.streamType) {
StreamType.CAMERA -> renderLocalCamera(stream)
StreamType.SCREEN_SHARE -> renderLocalScreen(stream)
StreamType.MULTI_CAMERA -> renderSecondaryCamera(stream)
}
}
}
}
Observe Remote Streams
lifecycleScope.launch {
streamRepository.subscribedStreams.collect { remoteStreams ->
// Group by participant
val participants = remoteStreams.groupBy { it.endpointId }
participants.forEach { (endpointId, streams) ->
// Each participant may have multiple streams (camera + screen)
val cameraStream = streams.find { it.streamType == StreamType.CAMERA }
val screenStream = streams.find { it.streamType == StreamType.SCREEN_SHARE }
cameraStream?.let { renderRemoteCamera(it) }
screenStream?.let { renderRemoteScreen(it) }
}
}
}
Picture-in-Picture (PiP)
The SDK automatically maintains streams during PiP transitions. To enable PiP:
- Add PiP configuration to your Activity in
AndroidManifest.xml(see Setup Instructions) - Handle PiP enter/exit in your Activity:
override fun onUserLeaveHint() {
super.onUserLeaveHint()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val params = PictureInPictureParams.Builder()
.setAspectRatio(Rational(16, 9))
.build()
enterPictureInPictureMode(params)
}
}
The SDK's reactive state automatically survives PiP transitions without interrupting streams.
Text fields under the call
When the call goes into picture-in-picture or back to full screen, your screen underneath gets focus back, and Android restores the keyboard of any text field that still has focus. The PiP window moves with the keyboard, and on some devices (Samsung) the keyboard is shown, hidden and shown again, so the window jumps. The SDK leaves your screens alone, so clear the field's focus before starting the call and whenever the customer leaves a field:
currentFocus?.let { view ->
getSystemService(InputMethodManager::class.java).hideSoftInputFromWindow(view.windowToken, 0)
view.clearFocus()
}
Hiding the keyboard isn't enough on its own: a field that keeps focus gets its keyboard back. Something else in the layout must be able to take focus (for example android:focusableInTouchMode="true" on the field's container), or Android gives focus back to the first field.
Configuration Options (Pre-Built UI)
When using AuviousSimpleConferenceActivity, you can configure:
Call Mode
callMode: The stream published when joining, one of theAuviousCallModevalues:"audio"(microphone only),"video"(camera only) or"audio_video". The customer can open the missing camera or microphone during the call, and the stream is republished with both. When set,microphoneOptionandcameraOptionare ignored.
Enable/Disable Features
microphoneOption: Publish the microphone when joining (true/false, ignored whencallModeis set)cameraOption: Publish the camera when joining (true/false, ignored whencallModeis set)speakerOption: Enable speaker (true/false,false= earpiece only)participantName: The name shown to the other participants
Show/Hide Control Buttons
microphoneAvailable: Show microphone toggle button (true/false)cameraAvailable: Show camera toggle button (true/false)speakerAvailable: Show speaker toggle button (true/false)
UI Customization
conferenceBackgroundColor: Custom background color, as a color int string (e.g."#3366ff".toColorInt().toString(); default#232931)
Picture-in-Picture
autoEnterPip: Enter PiP automatically when the customer leaves the app (true/false, defaulttrue)pipAvailability: Show the "Floating window" menu item (true/false, defaulttrue). Screen sharing always enters PiP.
Screen Sharing
screenShareAvailability: Show the screen share option (true/false, defaulttrue)allowsEntireScreenShare: Allow sharing the entire screen on Android 14+ (true/false, defaultfalse). Shares of another app are always stopped. Whenfalse, only this app may be shared: an entire-screen share is stopped once the customer leaves the app, and they're asked to share again picking this app.
Co-browse in the call
cobrowseAvailable: Let the agent co-browse the call (true/false, defaultfalse). When the agent asks, the customer shares this app's screen from the call; the call shrinks to picture-in-picture and the agent never sees it.cobrowseAnnotationsEnabled,cobrowseShowsMaskHints,cobrowseShowsShareExplainer: Same as the co-browse configuration (defaulttrue)
Masking follows AuviousCobrowse.maskingRules. Set AuviousCobrowse.inCallListener before starting the call to open the screen the agent should see once sharing starts (AuviousCobrowseState.SHARING), and to go back in onCobrowseEnded. Open it from your own screen once it's back on top (the call is then in picture-in-picture): opened while the call is still full screen, it goes into the PiP window with the call.
AR Pointer
arPointerAvailable: Let the agent point and draw on the customer's camera and on a regular screen share, never on a co-browse screen (true/false, defaulttrue). The customer sees the points and ink and can annotate back.
Remote Logging
AuviousSimpleConferenceOptions also takes errorReporting (Auvious, Custom(dsn), Disabled; default Auvious) and logLevel (MINIMAL, FULL; default MINIMAL); see Remote Error Reporting.
Mobile Co-browse
Co-browse lets the customer share this app's screen with an agent, without a video call. Both sides can draw on the shared screen, and sensitive views are sent as black boxes. The session UI is drawn over every activity of the app, so the customer can keep navigating while sharing.
val config = AuviousCobrowseConfiguration(
ticket = pin, // the PIN created by the agent
baseEndpoint = "https://auvious.video",
mqttEndpoint = "wss://auvious.video/ws",
participantName = "Jane", // optional
masking = AuviousMaskingRules(identifiers = setOf("payment.iban", "card.*"))
)
AuviousCobrowse.start(activity, config, object : AuviousCobrowseListener {
override fun onCobrowseStateChanged(state: AuviousCobrowseState) { /* CONNECTING, AWAITING_CONSENT, SHARING, ... */ }
override fun onCobrowseEnded(reason: AuviousCobrowseEndReason) { /* CUSTOMER_ENDED, AGENT_ENDED, FAILED */ }
override fun onCobrowseFailed(error: AuviousCobrowseError) { /* INVALID_TICKET, NETWORK_ERROR, ... */ }
})
// End it from the app
AuviousCobrowse.stop()
Call AuviousCobrowse from the main thread. Only one session runs at a time, and not alongside a video call: to co-browse during a call, use the call's cobrowseAvailable option instead.
Other AuviousCobrowseConfiguration options: clientId (default "customer"), customerMetadata, showsShareExplainer, annotationsEnabled, showsMaskHints (all default true), allowsEntireScreenShare (default false) and accentColor (the sharing border, status pill and End button; default the SDK's red gradient).
On Android 14+ only this app may be shared unless allowsEntireScreenShare is set. Otherwise frames are dropped while the app is in the background, so other apps never reach the agent. When it is set, the screen outside the app is shared too, without masks.
Masking
A view is masked when, in order of precedence:
- it is marked
auviousMasked = false→ never masked, - it is marked
auviousMasked = true, - its id resource name or String tag matches
identifiers(exact, or a prefix ending with*), - it is an instance of one of
viewClasses, - a default applies:
maskSecureTextFields(password fields and the keyboard while one is focused, defaulttrue) ormaskAllTextInputs(everyEditText, defaultfalse).
// Views
cardNumberView.auviousMasked = true
// Jetpack Compose (identifiers, classes and text input defaults only apply to Views)
Text(iban, modifier = Modifier.auviousMasked())
// Change the rules at any time, also while sharing
AuviousCobrowse.maskingRules = AuviousMaskingRules(
identifiers = setOf("card.*"),
viewClasses = listOf(CardNumberField::class.java),
maskAllTextInputs = true
)
// Fill every masked region red on the device to check your rules
AuviousCobrowse.debugHighlightMasks = true
Mask the smallest container that holds the sensitive content (the field, not the form).
Network Recovery
When the network drops during a call, the SDK reconnects MQTT and recovers the call's streams once the network is back, with no action from the host app. Streams whose connection comes back on its own are left alone, and the recovery never blocks hanging up.
Remote Error Reporting
The SDK reports errors to Sentry. Configure it through AuviousSdkConfig (or AuviousSimpleConferenceOptions):
errorReporting: where reports go:AuviousErrorReporting.Auvious(Auvious's Sentry project, default),AuviousErrorReporting.Custom(dsn)(your own) orAuviousErrorReporting.DisabledlogLevel: what is sent:MINIMAL(crashes only, default) orFULL
AuviousSimpleConferenceOptions(
// ...
errorReporting = AuviousErrorReporting.Custom("https://<key>@<your-sentry>/<project>"),
logLevel = LogLevel.MINIMAL
)
Only crashes and ANRs with a frame in the SDK's code (com.auvious.*, org.webrtc.*) are reported; the rest of the app's crashes are not. Native (NDK) crashes are not reported.
Sentry starts with the app, before this config is passed in. To set where reports go from the start, or to keep Sentry off entirely, also add to your AndroidManifest.xml (auvious, disabled, or your own DSN):
<meta-data
android:name="com.auvious.auvioussdk.error_reporting"
android:value="disabled" />