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.

MeetoWidgetConfig fields
FieldPurpose
keyRequired public key mk_live_…
roomNamePersistent named room shared by everyone using it, such as support
roomCode 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 / accentPrefilled display name, interface language and hex accent color
autoJoinSkip the lobby in open-door rooms; default false
baseUrlThe 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.

Events and commands
NameDetails
ready / phaseThe widget loaded; phase reports prejoin, room, finished or error
joined / leftpayload has room and identity; left has reason and durationSec
participants / errorpayload 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 limits for calls in the SDK
PlanLimit
FreeUp to 10 participants and 60-minute calls once two people are in
ProUp to 25 participants; no time limit for up to 2 simultaneous meetings per account
Every planThe 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.