This SDK gives you a ready-to-use chat UI + chat core for Android (Jetpack Compose).
Part of the Ethora SDK ecosystem — see all SDKs, tools, and sample apps. Follow cross-SDK updates in the Release Notes.
JitPack build page (already available): https://jitpack.io/#dappros/ethora-sdk-android/v1.0.0
Project-level settings.gradle.kts:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven(url = "https://jitpack.io")
}
}App module build.gradle.kts:
dependencies {
implementation("com.github.dappros:ethora-sdk-android:1.0.19")
}Replace 1.0.19 with the latest tag. You can also pin to a specific commit hash instead of a tag.
The SDK uses Java 8+ date/time APIs (java.time.*). Android SDK < 26 requires desugaring — skip this and you'll get a runtime crash on older devices.
In your app module build.gradle.kts:
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
isCoreLibraryDesugaringEnabled = true // ← add this
}
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") // ← add this
implementation("com.github.dappros:ethora-sdk-android:1.0.19")
}Note:
desugar_jdk_libsversion2.1.4is the minimum required. Always check the releases page for the latest.
Use this if you do not want JitPack and want SDK sources inside your app repo.
Copy these folders from this repo into your project root:
ethora-componentchat-corechat-ui
Keep this structure so ethora-component can find shared sources:
<your-project>/ethora-component<your-project>/chat-core<your-project>/chat-ui
In your app project settings.gradle.kts:
include(":ethora-component")In app module build.gradle.kts:
dependencies {
implementation(project(":ethora-component"))
}Same as the JitPack install path — the SDK uses java.time.* APIs and requires desugaring on API < 26:
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
isCoreLibraryDesugaringEnabled = true // ← add this
}
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") // ← add this
implementation(project(":ethora-component"))
}In AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />The most reliable integration flow is:
- Create
ChatConfiginremember(...). - Apply config with
ChatStore.setConfig(...). - Set API URL/token with
ApiClient.setBaseUrl(...). - Render
Chat(...).
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.fillMaxSize
import com.ethora.chat.Chat
import com.ethora.chat.core.config.AppConfig
import com.ethora.chat.core.config.ChatConfig
import com.ethora.chat.core.config.ChatHeaderSettingsConfig
import com.ethora.chat.core.config.JWTLoginConfig
import com.ethora.chat.core.config.XMPPSettings
import com.ethora.chat.core.networking.ApiClient
import com.ethora.chat.core.store.ChatStore
@Composable
fun EthoraChatScreen(
singleRoomJid: String,
dnsOverrides: Map<String, String>?,
loggedInUser: com.ethora.chat.core.models.User?,
hasJwtToken: Boolean
) {
val appConfig = remember(loggedInUser, dnsOverrides) {
ChatConfig(
appId = BuildConfig.APP_ID,
baseUrl = BuildConfig.API_BASE_URL,
disableRooms = true,
chatHeaderSettings = ChatHeaderSettingsConfig(
roomTitleOverrides = mapOf(singleRoomJid to "Playground Room 1"),
chatInfoButtonDisabled = true,
backButtonDisabled = true
),
xmppSettings = XMPPSettings(
xmppServerUrl = BuildConfig.XMPP_DEV_SERVER,
host = BuildConfig.XMPP_HOST,
conference = BuildConfig.XMPP_CONFERENCE
),
dnsFallbackOverrides = dnsOverrides,
// userLogin = loggedInUser?.let { user ->
// UserLoginConfig(enabled = true, user = user)
// },
jwtLogin = if (hasJwtToken) {
JWTLoginConfig(
token = BuildConfig.USER_TOKEN,
enabled = true
)
} else null,
defaultLogin = false,
customAppToken = BuildConfig.API_TOKEN
)
}
ChatStore.setConfig(appConfig)
ApiClient.setBaseUrl(appConfig.baseUrl ?: AppConfig.defaultBaseURL, appConfig.customAppToken)
Chat(
config = appConfig,
roomJID = singleRoomJid,
modifier = Modifier.fillMaxSize()
)
}disableRooms = true+roomJIDinChat(...)gives you single-room mode.jwtLoginis used whenenabled = trueand token is provided.chatHeaderSettings.roomTitleOverrideslets you replace raw JID in header.dnsFallbackOverrideshelps when emulator DNS cannot resolve your hosts.customAppTokenis forwarded viaApiClient.setBaseUrl(...).
SDK exports a composable hook:
useUnread(maxCount: Int = 10): UnreadStateUnreadState.totalCount(Int)UnreadState.displayCount(String, for example10+)
import androidx.compose.material3.Badge
import androidx.compose.material3.BadgedBox
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import com.ethora.chat.useUnread
@Composable
fun ChatTabBadge() {
val unread = useUnread(maxCount = 99)
BadgedBox(
badge = {
if (unread.totalCount > 0) {
Badge { Text(unread.displayCount) }
}
}
) {
Text("Chat")
}
}- The unread state comes from SDK
RoomStore. - Unread values are meaningful after chat data is loaded (the
Chat(...)flow has initialized rooms/session). - If chat is not initialized yet, unread will be
0.
./gradlew :ethora-component:assembleThis repo includes jitpack.yml and is configured for:
groupId:com.github.dapprosartifactId:ethora-sdk-android
Client dependency format:
implementation("com.github.dappros:ethora-sdk-android:1.0.19")