Android SDK
Тонкий клиент на Kotlin и Jetpack Compose, который открывает чат поддержки Respondo в виде нижнего листа (bottom sheet) поверх вашего приложения.
Требования#
minSdk24 (Android 7.0), compileSdk 35.- Kotlin 2.x и Jetpack Compose (SDK поставляет Compose-интерфейс).
- JVM target 17.
SDK сам подтягивает свои транзитивные зависимости (Coroutines, kotlinx.serialization, OkHttp, Coil, Compose) и объявляет разрешение INTERNET в своём манифесте — вручную добавлять ничего не нужно.
Установка#
Библиотека опубликована в Maven Central как ai.respondo:respondo-sdk. Убедитесь, что mavenCentral() есть в ваших репозиториях (в новом Android-проекте он там по умолчанию), затем добавьте зависимость по координате.
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}dependencies {
implementation("ai.respondo:respondo-sdk:0.1.0")
}Инициализация#
Вызовите Respondo.init один раз, смонтируйте RespondoChatHost() один раз в своём дереве Compose и открывайте чат из собственной кнопки.
import ai.respondo.sdk.Respondo
import ai.respondo.sdk.RespondoConfig
import ai.respondo.sdk.ui.RespondoChatHost
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Respondo.init(
context = this,
config = RespondoConfig(
agentId = "<agent-uuid>", // из панели управления
channelId = "<channel-uuid>", // необязательно
// baseUrl опущен -> https://api.respondo.ai
),
)
setContent {
MaterialTheme {
Box(modifier = Modifier.fillMaxSize()) {
Button(onClick = { Respondo.open() }) { Text("Support") }
RespondoChatHost() // SDK показывает лист, когда состояние чата OPEN
}
}
}
}
}Все вызовы идут через синглтон Respondo. Они идемпотентны, безопасны из любого потока, а вызовы, сделанные до завершения init, буферизуются и переигрываются.
Идентификация пользователей#
По умолчанию каждый посетитель анонимен. При первом запуске SDK генерирует стабильный visitor_id и держит его в локальном хранилище, поэтому вернувшийся пользователь снова находит свой диалог. Никакого API-ключа встраивать не нужно — каждый диалог защищён session-токеном на диалог, который backend выпускает и сдвигает вперёд на каждом сообщении, так что анонимному посетителю никогда не приходится проходить аутентификацию заново.
Вызовите identify, как только пользователь вошёл. Это привязывает его настоящую личность, поэтому его история следует за ним между устройствами и переустановками, а имя и email показываются рядом с диалогом в вашем инбоксе вместо анонимного посетителя.
userHash — это подпись, которую ваш backend вычисляет из identity_secret агента — HMAC-SHA256(secret, userId) (или email, если userId нет), закодированная как hex в нижнем регистре. Секрет доказывает подлинность личности, поэтому он должен жить только на вашем backend и никогда не поставляться в приложении. Полную формулу и серверные примеры смотрите на странице Проверка личности.
import ai.respondo.sdk.RespondoIdentity
// userHash приходит с вашего backend (HMAC-SHA256 по userId) —
// никогда не вычисляйте его в приложении.
Respondo.identify(
RespondoIdentity(
userId = session.userId,
email = session.email,
name = session.fullName,
userHash = session.respondoUserHash,
),
)
// При выходе: сбросьте пуш-токен, отзовите сессию, начните нового анонимного посетителя.
Respondo.clearPushToken()
Respondo.reset()Если хеш отсутствует или неверен, ничего не падает: backend молча оставляет посетителя анонимным, чат продолжает работать, и вы просто теряете кросс-девайс-связь, пока не будет передан валидный хеш. Проверка запускается только тогда, когда у агента задан identity_secret — оставьте его пустым при разработке, и userId / email будут приняты как есть.
Наблюдаемые значения и коллбэки#
Читайте состояние реактивно как StateFlow (отлично для Compose) или императивно через RespondoListener (отлично для бейджа на иконке приложения).
// Реактивно: число непрочитанных как StateFlow.
val unread by Respondo.unreadCount.collectAsState()
// Императивно: слушатель для бейджей и аналитики.
Respondo.setListener(object : RespondoListener {
override fun onUnreadChanged(count: Int) { updateAppIconBadge(count) }
override fun onUrlRequested(url: String): Boolean = tryOpenInternally(url)
})Отслеживание экрана#
Сообщайте текущий экран при каждой навигации, чтобы проактивные тизеры и таргетинг уровня страницы могли по нему срабатывать. Передайте null, чтобы очистить его; при каждом изменении проактивный тизер пересчитывается для нового экрана.
Respondo.setCurrentScreen("pricing")Дальнейшие шаги#
Настройте Пуш-уведомления для офлайн-ответов операторов и Проверку личности для вошедших пользователей. Полная поверхность API, обёртка Fragment для XML-хостов и решение проблем разобраны в гайде по началу работы с Android SDK; исходники SDK открыты на GitHub.