Android Chatbot SDK

View Release Notes →

dialogs_Chatbot_Android embeds a fully functional EnableX Dialogs chatbot into any Android application. It uses a WebView-Bridge architecture — all chat protocol logic runs inside an embedded WebView loading a remote chat page, while a @JavascriptInterface-annotated bridge class (AndroidBridge) connects JS events to Kotlin/Java code.

Android Chatbot SDK v1.0.0  ·  Released June 25, 2026

First release of the Android Chatbot SDK. See Release Notes for what's included.

  Download Android Chatbot SDK v1.0.0
Setup

Integration Surfaces

Four integration surfaces are provided — pick the one that matches how your app is built:

SurfaceClassUse When
XML ViewEnxChatClientDrop into any XML layout, or create programmatically
FragmentEnxChatFragmentFragment back-stack, multi-pane layouts
Headless SessionEnxChatSessionCustom Compose or View UI; no built-in UI needed
ComposeEnxChat composablePure Jetpack Compose screens

Requirements

ToolMinimum
Android StudioHedgehog (2023.1.1) or newer
Gradle8.x
Android Gradle Plugin8.x
minSdkVersion24 (Android 7.0)
compileSdkVersion36 (Android 15)
Java11
Kotlin1.9+

Installation

Add to settings.gradle.kts

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        // Add local AAR or private maven if applicable
    }
}

Add Dependency

// build.gradle.kts (app module)
dependencies {
    implementation("com.dialogs:chatbot-android:1.0.0")
    // OR, for the local AAR download from this site:
    implementation(files("libs/dialogs_Chatbot_Android-1.0.0.aar"))
}

Required Permissions in AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

CAMERA and RECORD_AUDIO are only required for video/audio click-to-call. Both are runtime permissions on Android 6+; the SDK requests them automatically when a call is initiated.

ProGuard

The SDK ships proguard-rules.pro. If you use R8/ProGuard, ensure the JS bridge class is kept:

-keepclassmembers class com.dialogs_chatbot_android.bridge.AndroidBridge {
    public *;
}
Usage Patterns

Pattern 1 — Quickest (EnxChatClient + bindToLifecycle)

class ChatActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val chatView = EnxChatClient(this)
        chatView.bindToLifecycle(this)   // auto disconnect/release on lifecycle events
        chatView.start(
            config = EnxChatConfig(
                botId = "YOUR_BOT_ID",
                host = "https://dialogs.enablex.io"
            )
        )
        setContentView(chatView)
    }
}

Pattern 2 — Fragment Integration

val fragment = EnxChatFragment.newInstance(
    botId = "YOUR_BOT_ID",
    host = "https://dialogs.enablex.io",
    isCallView = true,
    enableHistory = true
)
fragment.setSessionListener(mySessionListener)

supportFragmentManager.beginTransaction()
    .replace(R.id.container, fragment)
    .addToBackStack(null)
    .commit()

Pattern 3 — Full Lifecycle Control (with Callbacks)

class ChatActivity : AppCompatActivity() {
    private lateinit var chatView: EnxChatClient

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        chatView = EnxChatClient(this)

        chatView.setSessionListener(object : EnxChatSessionListenerAdapter() {
            override fun onStateChanged(state: EnxChatState) {
                updateConnectionBanner(state)
            }
            override fun onBotMessage(chatMessage: ChatMessage) {
                handleMessage(chatMessage)
            }
            override fun onVideoCallRequested(iFrameUrl: String) {
                // EnxVideoCallActivity is launched automatically by the SDK
            }
            override fun onError(error: String) {
                Log.e("Chat", error)
            }
        })

        chatView.setUiListener(object : EnxChatBotListener() {
            override fun onBotConnected(botInfo: BotInfo) {
                supportActionBar?.title = botInfo.name
            }
            override fun onClose() { finish() }
        })

        chatView.start(EnxChatConfig("YOUR_BOT_ID", "https://dialogs.enablex.io"))
        setContentView(chatView)
    }

    override fun onDestroy() {
        chatView.release()
        super.onDestroy()
    }
}

Pattern 4 — Java Integration

EnxChatConfig config = new EnxChatConfig("YOUR_BOT_ID", "https://dialogs.enablex.io");
EnxChatClient chatView = new EnxChatClient(this);
chatView.bindToLifecycle(this);
chatView.start(config, new EnxChatBotListener() {
    @Override public void onBotConnected(BotInfo botInfo) { }
    @Override public void onClose() { finish(); }
});
setContentView(chatView);

Pattern 5 — Headless Session (Custom UI)

val session = EnxChatSession.create(this, config)
session.addSessionListener(object : EnxChatSessionListenerAdapter() {
    override fun onBotMessage(chatMessage: ChatMessage) {
        // Update your own UI
        myAdapter.add(chatMessage)
    }
})
session.initializeAndConnect()

// Send from your own input:
binding.sendBtn.setOnClickListener {
    session.sendMessage(binding.input.text.toString())
}

// Cleanup:
override fun onDestroy() {
    session.release()
    super.onDestroy()
}

Pattern 6 — Jetpack Compose

@Composable
fun ChatScreen() {
    EnxChat(
        config = EnxChatConfig(
            botId = "YOUR_BOT_ID",
            host = "https://dialogs.enablex.io"
        ),
        modifier = Modifier.fillMaxSize()
    )
}
Configuration

Configuration Reference

data class EnxChatConfig(
    val botId: String,                 // Required — from EnableX Dialogs dashboard
    val host: String,                  // Required — e.g. "https://dialogs.enablex.io"
    val path: String = "",             // Optional URL prefix
    val isCallView: Boolean = false,   // Show call buttons in header
    val enableHistory: Boolean = false // Load previous messages on connect
)
Handling Messages

Message Type Handling

override fun onBotMessage(chatMessage: ChatMessage) {
    when (chatMessage.messageType) {
        MessageType.TEXT        -> renderText(chatMessage.text)
        MessageType.QUICK_REPLY -> renderChips(chatMessage.quickReplies, chatMessage.allowMultiple)
        MessageType.BUTTONS     -> renderButtons(chatMessage.buttons)
        MessageType.IMAGE       -> renderImage(chatMessage.attachments.first().url)
        MessageType.VIDEO       -> renderVideo(chatMessage.attachments.first().url)
        MessageType.AUDIO       -> renderAudio(chatMessage.attachments.first().url)
        MessageType.FILE        -> renderFileCard(chatMessage.attachments.first())
        MessageType.CARD        -> renderCard(chatMessage)
        MessageType.CAROUSEL    -> renderCarousel(chatMessage)
        MessageType.SYSTEM      -> renderSystemNotice(chatMessage.text)
        MessageType.UNKNOWN     -> Log.w("Chat", chatMessage.rawData.toString())
    }
}

Sending Messages Programmatically

// Plain text
session.sendMessage("What are your hours?")

// With attachment
val attachment = ChatAttachment(
    uri = fileUri,
    type = AttachmentType.DOCUMENT,
    fileName = "report.pdf",
    mimeType = "application/pdf"
)
session.sendMessage("Please see the attached file", listOf(attachment))

// Reset conversation
session.resetConversation()
Video Call Integration

Mode A — SDK-Managed (Default)

When the bot initiates a video call, EnxVideoCallActivity is launched automatically. No extra code is needed.

override fun onVideoCallRequested(iFrameUrl: String) {
    // Called regardless, even though the SDK also launches the activity — use for analytics
    analytics.track("video_call_started")
}

override fun onVideoCallEnded() {
    analytics.track("video_call_ended")
}

Mode B — Custom Video UI

Intercept the callback and route the call URL into your own video solution instead:

override fun onVideoCallRequested(iFrameUrl: String) {
    MyVideoSDK.join(iFrameUrl)
}
WebView Bridge

JS Interface Reference

The AndroidBridge is registered on the chat WebView as window.Android.

JS Can Call (Android Exposes)

// Query
Android.isReady()           // returns Boolean
Android.getConfig()         // returns JSON: {botId, host, path}

// Events JS fires → Android
Android.onChatReady(json)
Android.onConnected(json)
Android.onConversationReady(json)
Android.onMessageReceived(json)
Android.onMessageSent(json)
Android.onTyping(json)
Android.onError(json)
Android.onDisconnected(json)
Android.onBotInfo(json)
Android.onReconnecting(json)
Android.onReconnected(json)
Android.onReconnectFailed(json)
Android.onClear(json)
Android.onConversationReset(json)
Android.onFileSent(json)
Android.onVideoCallRequested(url)
Android.onVideoCallEnded()

Android Injects into JS

initializeEnxChat({botId, host, path})  // or initializeChat(...) depending on chat page build
sendMessageToBot(text, attachments)
sendFileToBot(base64, fileName, mimeType)
resetConversation()
disconnectChat()
Lifecycle

Lifecycle Binding Reference

Lifecycle EventdisconnectOnStop=truereleaseOnDestroy=true
ON_STOPdisconnect()
ON_DESTROYrelease()
// Safest — auto-handles everything
chatView.bindToLifecycle(
    owner = this,
    disconnectOnStop = false,   // keep the connection alive when app is backgrounded
    releaseOnDestroy = true     // always release resources on destroy
)
Feature Support

Feature Support (v1.0.0)

FeatureSupportedNotes
Full chat UI (Jetpack Compose)YesMessage list, input bar, bot avatar/name/startup message from BotInfo, animated typing indicator.
Markdown renderingYesBold, italic, tables, strikethrough in bot messages.
Text messagesYesPlain text, user ↔ bot.
Quick replies / buttonsYesRendered as chips/buttons; allowMultiple supported on quick replies.
Image / video / audio / file attachmentsYesBoth incoming (bot) and outgoing (in-app picker).
Cards and carouselsYesStructured message rendering.
Calendar (date picker) promptsYes 
System / handover status messagesYes 
Click-to-call (text / audio / video)YesServer-driven via BotInfo.clickToCall — no extra integration code required. Minimal call screen and full-screen EnxVideoCallActivity both provided.
Conversation resetYesEnxChatClient.resetConversation() / EnxChatSession.resetConversation().
Conversation history restoreYesOptional via enableHistory in EnxChatConfig.
Lifecycle-safe auto disconnect/releaseYesVia bindToLifecycle().
Multi-bot poolingNoSingle bot instance per EnxChatClient / EnxChatSession in this milestone.
UI theming APINoNo theming beyond header color and profile picture in v1.0.
Offline message queueNoMessages fail while connection state is DISCONNECTED; monitor onStateChanged and prompt reconnect.
Reference

Known Limitations

LimitationImpactWorkaround
minSdk 24Android 7.0+ onlyNot supported on older devices
WebView-dependentRequires system WebView ≥ 67Prompt users to keep WebView updated
Mixed content allowedHTTP assets can load inside the HTTPS chat pageServe all chat assets over HTTPS in production
Video call is portrait-onlyEnxVideoCallActivity locks to portraitOverride orientation in your own activity if landscape is required
No offline queueMessages fail when DISCONNECTEDMonitor onStateChanged and prompt reconnect
JS bridge callbacks run off the main threadAndroidBridge callbacks are not guaranteed on the UI threadAlways wrap UI updates in runOnUiThread {}

Troubleshooting

Chat Stuck on Loading

  • Verify botId and host are correct
  • Check the INTERNET permission is declared in the manifest
  • Confirm the device has network access
  • Check the onError callback for JS bridge errors

Messages Appear Twice

Do not call sendMessage() from both EnxChatSession and EnxChatClient against the same underlying session.

Video Call Doesn't Open

  • Confirm CAMERA and RECORD_AUDIO permissions are granted at runtime
  • Verify android:hardwareAccelerated="true" is set on your <application> tag

IllegalStateException on WebView.destroy()

Always call release() after disconnect(), never before — releasing tears down the underlying WebView.

Callback Not Reflected in UI

override fun onBotMessage(chatMessage: ChatMessage) {
    runOnUiThread {
        myAdapter.notifyDataSetChanged()
    }
}

ProGuard Stripping the Bridge

-keepclassmembers class com.dialogs_chatbot_android.bridge.AndroidBridge { public *; }

Key Libraries

LibraryPurpose
AndroidX Core KTXKotlin extensions
Jetpack Compose (Material3)Full chat UI toolkit
Lifecycle ViewModel + StateFlowState management
MarkwonMarkdown rendering in message bubbles
CoilImage loading and caching
Activity Result APICamera / gallery / file pickers (built-in)