Android video call SDK
Add a video call to an Android app
meeto-widget-android is a Kotlin library, distributed through JitPack, that shows a Meeto video call inside your Android app. It wraps Meeto's hosted call widget in an Android WebView and adds a native bridge for events and commands. It needs minSdk 24, camera and microphone permissions granted at runtime, and a public mk_live_… key that allows app:// plus your applicationId.
How does the Android SDK work?
The SDK wraps the hosted Meeto widget in a web view; it is not a native video engine. MeetoWidgetView is a FrameLayout holding a system WebView that loads https://meeto.me/embed with your key. A JavaScript interface carries events to Kotlin, and commands go back into the page through evaluateJavascript. Every call runs through Meeto's media servers and is encrypted in transit, not end to end.
The wrapper keeps the WebView on Meeto: navigating to any other origin opens the system browser instead of loading that page in your app. The call behaves like Meeto in a mobile browser. The screen-share button appears only where the web view offers screen capture to pages, which the Android WebView does not, so people in your app see screens others share rather than share their own. For simply joining a meeting, a browser link may be all your users need.
Install from JitPack
Add the JitPack repository and the dependency. The library is version 0.1.3, built with compileSdk 35 and Java 17, and depends only on androidx.webkit and the Kotlin standard library.
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven("https://jitpack.io")
}
}
// app/build.gradle.kts
dependencies {
implementation("com.github.meeto-app:meeto-widget-android:0.1.3")
}Set up the key and permissions
An app has no web origin, so the SDK presents itself as app:// plus your applicationId. In Settings → Website widget, create a key and add that origin, for example app://com.example.myapp. Build variants with an applicationIdSuffix need their own entry.
Declare four permissions in the app manifest. The library declares none itself.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />Request CAMERA and RECORD_AUDIO at runtime before you load the call. The SDK grants the WebView's camera or microphone request only if your app already holds that permission; it never works around the system prompt. An app origin is declared by the SDK rather than checked by a browser, so treat the mk_live_… key as public and keep mk_secret_… on your server.
Show a call in an activity
Create the view, attach it, ask for permissions, then call load() with a configuration. Release the WebView in onDestroy.
import android.Manifest
import android.os.Bundle
import android.util.Log
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import me.meeto.widget.MeetoWidgetConfig
import me.meeto.widget.MeetoWidgetView
class CallActivity : AppCompatActivity() {
private lateinit var widget: MeetoWidgetView
private val askPermissions = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { showCall() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
widget = MeetoWidgetView(this)
setContentView(widget)
widget.onEvent = { event ->
when (event.type) {
"joined" -> Log.d("Meeto", "joined ${event.payload.optString("room")}")
"left" -> finish()
"error" -> Log.w("Meeto", event.payload.optString("code"))
}
}
askPermissions.launch(arrayOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO))
}
private fun showCall() {
widget.load(MeetoWidgetConfig(key = "mk_live_…", roomName = "support", userName = "Anna"))
}
override fun onDestroy() {
widget.destroy()
super.onDestroy()
}
}If the person declines, the call still loads, but the WebView gets no camera or microphone. To move to another room, call load() again with a new configuration.
| Field | Purpose |
|---|---|
| key | Required public key mk_live_… |
| roomName | Persistent named room shared by everyone using it, such as support |
| room | Code of an existing open room, such as the slug POST /api/embed/rooms returns to your server; rooms of /api/v1 meetings ask for a password the SDK cannot pass |
| userName / lang / accent | Prefilled display name, interface language and hex accent color |
| autoJoin | Skip the lobby in open-door rooms; default false |
| baseUrl | The Meeto address; defaults to https://meeto.me |
Events and commands
Events reach onEvent on the main thread as MeetoEvent(type, payload), where payload is the full JSONObject of the widget protocol, the same events the JavaScript SDK emits. Commands can be called from any thread.
| Name | Details |
|---|---|
| ready / phase | The widget loaded; phase reports prejoin, room, finished or error |
| joined / left | payload has room and identity; left has reason and durationSec |
| participants / error | payload has count; error has code and message |
| join(name) / leave() | Join from the lobby of an open-door room, or leave the call |
| setMicrophone(on) / setCamera(on) | Turn the microphone or camera on or off during the call |
In a room with knocking, a password or a lock, join() keeps the person in the lobby and the widget sends an error event with the code join_not_allowed.
Limits and production checklist
Register every applicationId
Release, debug and flavor builds with different IDs each need an app:// origin on the key.
Keep the WebView current
Calls depend on the device's Android System WebView. Test on an old and a current device.
Test two participants
Join the same room from a browser on another device and check joined, participants and left events.
| Plan | Limit |
|---|---|
| Free | Up to 10 participants and 60-minute calls once two people are in |
| Pro | Up to 25 participants; no time limit for up to 2 simultaneous meetings per account |
| Every plan | The SDK is free; the Powered by Meeto badge stays visible |
Limits follow the plan of the account that owns the key. Recording and AI summaries are started by a Pro or Team host; see pricing.
Frequently asked questions
Is meeto-widget-android a native WebRTC SDK?
No. It wraps Meeto's hosted call page in the system WebView and adds a Kotlin bridge for events and commands. Calls use the WebView's WebRTC and Meeto's media servers, so your APK carries no video engine.
Why is the camera black even though the permission is in the manifest?
CAMERA and RECORD_AUDIO are runtime permissions. The SDK grants the WebView's request only when your app already holds them, so request both before calling load(), as in the example on this page.
Does it work with Jetpack Compose?
MeetoWidgetView is a classic Android View, so host it in Compose with AndroidView: create it in the factory, call load() once and call destroy() when the composable leaves the screen.
What happens when the activity is recreated, for example on rotation?
A recreated activity builds a new view and loads the call again, and the visitor goes back through the lobby. Lock the call screen's orientation or handle the configuration change yourself if calls should survive rotation.
Put it to work
Open your Meeto account to set it up, or explore the working product first.