آموزش SDK اندروید هدهد: پشتیبانی بومی داخل اپ در ۱۰ دقیقه
چت و تیکت پشتیبانی را با SDK اندروید هدهد داخل اپ کاتلین و Compose بگذارید: نصب وابستگی، پیکربندی، باز کردن چت، شناسایی کاربر با HMAC، تم و زبان، شمارندهٔ پیام خواندهنشده و رفع خطاهای رایج.
در این مطلب
اپ اندرویدی دارید و مشتریهایتان داخل همان اپ سؤال میپرسند. سادهترین راه این است که ویجت وب را در یک WebView باز کنید؛ اما اگر ظاهر بومی، صفحهکلید و پیوستهای اندروید و حس یک اپ واقعی برایتان مهم است، SDK اندروید هدهد همین کار را بدون WebView انجام میدهد. این SDK با همان صندوق و همان API ویجت وب کار میکند؛ پس گفتوگوها و تیکتهای اپ هم در همان صندوق مشترک تیم میآیند.
در این آموزش یک اپ کاتلین را قدمبهقدم به SDK وصل میکنیم: نصب، پیکربندی، باز کردن چت، شناسایی کاربر، تم و زبان و شمارندهٔ پیام خواندهنشده. در آخر هم رفتار تیکت و نظرسنجی و رفع خطاهای رایج را میبینید.
نسخهٔ فعلی SDK بتا است (
1.0.0-beta04). نام کلاسها و پارامترهای زیر از همین نسخه گرفته شدهاند و ممکن است تا انتشار نسخهٔ پایدار تغییر کنند.
SDK یا ویجت داخل WebView؟
| ویجت وب در WebView | SDK اندروید | |
|---|---|---|
| ظاهر | صفحهٔ وب داخل اپ | رابط بومی Compose با همان طراحی ویجت هدهد |
| راستبهچپ و فونت فارسی | وابسته به صفحه | از اول راستبهچپ، با فونت وزیرمتن همراه SDK |
| پیوست و دوربین | محدود به WebView | انتخاب عکس و فایل از خود اندروید |
| شناسایی کاربر | از طریق جاوااسکریپت | یک فراخوانی کاتلین |
| شمارندهٔ خواندهنشده روی دکمه | ندارد | دارد |
| پوستهٔ روشن و تیره | وابسته به صفحه | با سیستم هماهنگ میشود یا خودتان تعیین میکنید |
| نصب | یک قطعهکد | وابستگی Gradle |
اگر اپ شما فقط یک صفحهٔ وب داخل پوستهٔ اندروید است، همان ویجت وب کافی است. اگر اپ بومی است و پشتیبانی بخشی از تجربهٔ آن است، SDK انتخاب بهتری است.
SDK چه چیزهایی دارد؟
- حالتهای تماس صندوق: چت زنده، تیکت، هر دو یا «تیکت بیرون از ساعت کاری». همان تنظیمی که برای ویجت وب در پنل گذاشتهاید، در اپ هم اعمال میشود.
- تیکت: فرم تیکت با موضوع، توضیح، دسته و پیوست (مطابق تنظیم صندوق)، فهرست تیکتهای من و رشتهٔ هر تیکت با نشان وضعیت و امکان پاسخ.
- نظرسنجی رضایت (CSAT): سه نمایش ایموجی، ستاره و عدد ۱ تا ۵، با کادر اختیاری برای توضیح.
- اطلاعیه و هشدار صندوق (از
1.0.0-beta04): همان اطلاعیههای ویجت وب بالای صفحهٔ شروع نشان داده میشوند، بدون کد اضافه. - فرم پیش از چت، ساعت کاری و تعطیلات، نوار اعلان مشکل (ایشو) و بستن گفتوگو یا ارسال رونوشت با ایمیل، همانطور که در ویجت وب هست.
- شش زبان: فارسی، انگلیسی، عربی، آلمانی، اسپانیایی و فرانسوی؛ زبانهای دیگر به انگلیسی برمیگردند.
- پوستهٔ روشن و تیره، رنگ برند صندوق و امکان تغییر دستی رنگ.
- قطع اتصال: با قطع شبکه یا وبسوکت نوار «اتصال برقرار نیست» نشان داده میشود، SDK دوباره وصل میشود و پیامی که ارسالش شکست خورده با دکمهٔ «تلاش دوباره» میماند.
پیشنیازها
- یک صندوق «وبسایت» در پنل هدهد. اگر هنوز ندارید، راهنمای نصب چت آنلاین را تا ساخت صندوق بخوانید.
- مقدار
websiteTokenهمان صندوق. در قطعهکد ویجت همین مقدار هست. minSdkبرابر ۲۴ یا بالاتر، کاتلین و Java 17. SDK باcompileSdkبرابر ۳۵ و Compose ساخته شده و رابطش Jetpack Compose است. اگر اپ شما هنوز با View کار میکند، باHodhod.openاز همان Activity آماده استفاده کنید؛ وابستگیهای Compose بهصورت خودکار اضافه میشوند.
قدم ۱: افزودن وابستگی
SDK دو ماژول دارد: hodhod-core (بدون رابط: API، اتصال زنده و منطق) و hodhod-ui (رابط Compose و Activity آماده). hodhod-ui خودش hodhod-core را هم میآورد، پس برای رابط آماده فقط همین یکی کافی است.
SDK متنباز (MIT) است و در github.com/HodHodChat/hodhod-android-sdk قرار دارد و از طریق JitPack نصب میشود؛ JitPack آن را از روی تگ گیت 1.0.0-beta04 مخزن میسازد. ابتدا مخزن JitPack را به settings.gradle.kts اضافه کنید:
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
سپس وابستگی را بگذارید:
// app/build.gradle.kts
dependencies {
implementation("com.github.HodHodChat.hodhod-android-sdk:hodhod-ui:1.0.0-beta04")
}
مختصات بسته com.github.HodHodChat.hodhod-android-sdk:hodhod-ui:1.0.0-beta04 است (و اگر فقط هستهٔ بدون رابط را میخواهید ...:hodhod-core:1.0.0-beta04). نام پکیجهای کاتلین در کد شما همچنان chat.hodhod.sdk است.
قدم ۲: پیکربندی در Application
SDK را یک بار و در Application.onCreate پیکربندی کنید. تا وقتی Hodhod.start()، Hodhod.open() یا Hodhod.identify() صدا نشده باشد، SDK هیچ درخواست شبکهای نمیفرستد.
import android.app.Application
import chat.hodhod.sdk.Hodhod
import chat.hodhod.sdk.HodhodConfig
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
Hodhod.configure(
this,
HodhodConfig(
baseUrl = "https://hodhod.chat", // your Hodhod server, no extra path
websiteToken = "YOUR_WEBSITE_TOKEN", // the website inbox token
),
)
Hodhod.start() // optional: so the unread counter works before the chat is opened
}
}
کلاس را در AndroidManifest.xml با android:name=".MyApp" به تگ <application> بدهید. آدرس پایه باید https:// باشد؛ فقط برای سرور محلی در حالت دیباگ، شرطش را در بخش «عیبیابی» ببینید.
قدم ۳: باز کردن چت
چهار راه دارید؛ از سادهترین شروع میکنیم.
۱. Activity آماده. از هر Activity یا Fragment، یک خط:
Hodhod.open(context)
این فراخوانی HodhodChatActivity را باز میکند؛ صفحهای تمامصفحه با تم مستقل از تم اپ شما که دکمهٔ برگشت را خودش مدیریت میکند. همین صفحه با لینک عمیق hodhod://chat هم باز میشود.
۲. جاسازی در صفحهٔ Compose خودتان. اگر میخواهید چت داخل ناوبری اپ باشد:
@Composable
fun SupportScreen(onBack: () -> Unit) {
HodhodChat(modifier = Modifier.fillMaxSize(), onClose = onBack)
}
HodhodChat صفحهٔ خانه، چت، تیکتها و فرم پیش از چت را بسته به تنظیم صندوق نشان میدهد و وقتی کاربر از صفحهٔ اول برمیگردد یا دکمهٔ بستن را میزند onClose را صدا میکند. فضای نوار وضعیت و نوار ناوبری را اگر لازم باشد خودتان در modifier رعایت کنید؛ HodhodChatActivity همین کار را با windowInsetsPadding(WindowInsets.systemBars) میکند.
۳. دکمهٔ شناور با شمارندهٔ خواندهنشده. HodhodBubble یک دکمهٔ گرد است که تعداد پیامهای خواندهنشده را روی خود نشان میدهد و با کلیک Hodhod.open را صدا میزند:
Box(Modifier.fillMaxSize()) {
// screen content
HodhodBubble(Modifier.align(Alignment.BottomEnd).padding(16.dp))
}
برای رفتار دیگر، onClick را هم بدهید. رنگ دکمه از صندوق میآید یا با پارامتر accent عوض میشود.
۴. دکمهٔ خودتان. هر دکمهای در طراحی اپ (مثلاً «پشتیبانی» در پروفایل) فقط Hodhod.open(context) را صدا بزند.
قدم ۴: شناسایی کاربر
اگر کاربر در اپ وارد شده است، او را به هدهد معرفی کنید تا گفتوگوها و تیکتها به نام خودش ثبت شوند و در دستگاه دیگر هم پیدا شوند:
Hodhod.identify(
HodhodUser(
identifier = user.id, // a stable user id in your system
identifierHash = hashFromBackend, // HMAC computed on your backend
name = user.name,
email = user.email,
phone = user.phone,
customAttributes = mapOf("plan" to "pro"),
)
) { result ->
result.onFailure { Log.w("Support", "identify failed: ${it.message}") }
}
بدون identifier، کاربر یک مخاطب ناشناس است. نتیجه روی ترد اصلی برمیگردد.
چرا identifierHash و چرا در بکاند؟
اگر هر کسی بتواند شناسهٔ دیگری را بفرستد، میتواند گفتوگوهای او را ببیند. برای جلوگیری، هدهد برای هر صندوق یک کلید محرمانهٔ HMAC دارد (در تنظیمات صندوق، بخش «تأیید هویت کاربر») و identifierHash را به این صورت چک میکند: HMAC با SHA-256 از identifier با آن کلید، بهصورت هگز.
این کلید نباید داخل اپ باشد. اپ از بکاند خودتان، بعد از ورود کاربر، فقط مقدار identifierHash را میگیرد. دو نمونه:
// Node.js
import { createHmac } from 'node:crypto';
const identifierHash = createHmac('sha256', process.env.HODHOD_HMAC_KEY)
.update(String(user.id))
.digest('hex');
# Python
import hashlib, hmac, os
identifier_hash = hmac.new(
os.environ["HODHOD_HMAC_KEY"].encode(),
str(user.id).encode(),
hashlib.sha256,
).hexdigest()
در PHP هم همین است: hash_hmac('sha256', (string) $user->id, $key). رشتهٔ identifier در اپ و در بکاند باید دقیقاً یکی باشد.
اگر در تنظیمات صندوق گزینهٔ «مجبور کردن تأیید اعتبار هویت کاربر» را روشن کنید، درخواست بدون identifierHash رد میشود. روشنکردنش را توصیه میکنیم تا کسی با حدسزدن شناسه به گفتوگوی دیگران نرسد.
ویژگیهای سفارشی و خروج
ویژگیهای سفارشی (مثلاً نسخهٔ اپ یا پلن) را همراه HodhodUser یا جدا بفرستید. تا وقتی نشست ساخته نشده، در صف میمانند:
Hodhod.setCustomAttributes(mapOf("app_version" to BuildConfig.VERSION_NAME))
وقتی کاربر از اپ خارج میشود:
Hodhod.logout()
این فراخوانی نشست و توکنهای ذخیرهشده را پاک میکند و اتصال را میبندد؛ استفادهٔ بعدی یک مخاطب ناشناس تازه میسازد. پس مخاطب بعدی که روی همان گوشی وارد میشود گفتوگوهای کاربر قبلی را نمیبیند.
قدم ۵: تم، زبان و حالت تیره
همهٔ اینها در HodhodConfig تنظیم میشوند:
HodhodConfig(
baseUrl = "https://hodhod.chat",
websiteToken = "YOUR_WEBSITE_TOKEN",
locale = "fa", // fa, en, ar, de, es or fr
darkMode = DarkMode.AUTO, // AUTO (system), LIGHT or DARK
accentColorOverride = 0xFF6A2BC4, // an ARGB color instead of the inbox widget color
)
- زبان: اولویت با
localeدر پیکربندی است، بعد زبان حساب در سرور و بعد زبان دستگاه. زبانی که پشتیبانی نشود، به انگلیسی برمیگردد. جهت صفحه (راستبهچپ برای فارسی و عربی) خودکار تعیین میشود. - پوسته:
AUTOاز تنظیم سیستم پیروی میکند. - رنگ: اگر
accentColorOverrideرا ندهید، رنگ ویجتی که در پنل برای صندوق تعیین کردهاید استفاده میشود.
قدم ۶: شمارندهٔ پیام خواندهنشده
تعداد پیامهای خواندهنشده یک StateFlow<Int> است. تا Hodhod.start() یا Hodhod.open() صدا نشده باشد، صفر میماند؛ برای همین start() را در قدم ۲ گذاشتیم. شمارنده تا وقتی اپ در حال اجراست زنده بهروز میشود.
val unread by Hodhod.unreadCount.collectAsState()
BadgedBox(badge = { if (unread > 0) Badge { Text("$unread") } }) {
Icon(Icons.Outlined.SupportAgent, contentDescription = "Support")
}
اگر HodhodBubble را به کار بردهاید، این شمارنده را خودش نشان میدهد. وضعیت بارگذاری تنظیمات هم در Hodhod.state است (Idle، Loading، Ready یا Failed).
رفتار تیکت و نظرسنجی در اپ
این رفتارها با تنظیم صندوق در پنل کنترل میشوند و کدی لازم ندارند:
- حالت تماس: در حالت «چت» مشتری مستقیم وارد گفتوگو میشود، در «تیکت» فرم تیکت میبیند و در «هر دو» خودش یکی را انتخاب میکند. در «تیکت بیرون از ساعت کاری»، تا وقتی تیم سر کار است چت زنده است و بعد از ساعت کاری فرم تیکت باز میشود. این حالت به ساعت کاری تعریفشده در صندوق وابسته است.
- تیکت: بعد از ثبت، شمارهٔ تیکت نشان داده میشود و مشتری از «تیکتهای من» وضعیت (باز، در حال بررسی، منتظر پاسخ شما، حلشده، بسته) را میبیند و در همان رشته جواب میدهد. برای تفاوت تیکت و چت، تیکتینگ یا چت زنده را بخوانید.
- نظرسنجی: بعد از بستهشدن گفتوگو، اگر نظرسنجی صندوق روشن باشد، کارت امتیاز ۱ تا ۵ در سبک تعیینشده برای صندوق (ایموجی، ستاره یا عدد) نشان داده میشود. اگر نتیجه برایتان مهم است، CSAT چیست را ببینید.
- بعد از پایان گفتوگو: مشتری به صفحهٔ شروع برمیگردد و بسته به حالت تماس میتواند چت یا تیکت تازهای باز کند.
- فرم پیش از چت: اگر برای صندوق روشن باشد، پیش از اولین گفتوگو نشان داده میشود. فرم پیش از چت را ببینید.
مسیر ربات چت
مسیر گفتوگوی ربات در SDK اندروید بهصورت بومی اجرا میشود و نیازی به کد اضافه ندارید. اگر صندوق مسیر فعال داشته باشد، اپ آن را روی صفحهٔ شروع نشان میدهد: هر ۱۷ نوع گره، متغیرها و شرطها، اعتبارسنجی ورودی و امتیازدهی. بازدیدکننده را میشود به اپراتور زنده یا بهصورت تیکت تحویل داد و همان تحلیلهای مسیرِ ویجت وب ثبت میشود. اگر گزینهٔ require_flow مسیر روشن باشد، کارت مستقیم «شروع گفتوگو» تا وقتی مسیر در دسترس است پنهان میماند (اگر مسیر بارگذاری نشود دوباره نمایش داده میشود). موتور مسیر با ۴۰ سناریوی همارزی در برابر موتور وب سنجیده شده (گامها، متغیرها و رویدادها یکسان) و صفحهها روی شبیهساز در فارسی (راستبهچپ) و انگلیسی بررسی شدهاند. ویرایش مسیر فقط در داشبورد انجام میشود.
اطلاعیهها و هشدارهای صندوق
از نسخهٔ 1.0.0-beta04، SDK اطلاعیههایی را که برای صندوق در پنل تعریف کردهاید خودش بالای صفحهٔ شروع اپ نشان میدهد و کدی لازم ندارد. تنظیمات همان تنظیمات ویجت وب است (تنظیمات صندوق، بخش «اطلاعیههای ویجت»)؛ برای ساختن و نوشتن آنها اطلاعیه و هشدار در ویجت پشتیبانی را بخوانید.
- کجا نشان داده میشود: بالای صفحهٔ اول، صفحهٔ تیکت وقتی نقش صفحهٔ اول را دارد، و فرم پیش از چت؛ بالاتر از مسیر ربات و کارتهای شروع. نوار اعلان ایشو بعد از آنها میآید.
- تا دو مورد، در دو سطح: «اطلاعیه» زرد با نماد اطلاعات و «هشدار» قرمز با نماد هشدار، در پوستهٔ روشن و تیره و راستبهچپ.
- متن و پیوند: بخش پررنگ و پیوند روی بخشی از متن. فقط
http،https،mailtoوtelباز میشوند، با برنامهٔ مناسب (مرورگر، شمارهگیر، ایمیل) و نه در WebView. - تصویر: یک تصویر
httpsبا توضیح برای TalkBack و پیوند اختیاری؛ اگر بارگذاری نشود پنهان میشود. - بستن: اطلاعیهٔ قابلبستن دکمهٔ بستن دارد و انتخاب کاربر روی همان دستگاه به یاد میماند. اگر در پنل متن، سطح یا گزینهٔ قابلبستن را تغییر بدهید، اطلاعیه دوباره نشان داده میشود.
- زمانبندی: سرور فقط اطلاعیههای روشن و داخل بازهٔ زمانی را میفرستد؛ SDK کاری برای آن ندارد.
- سرور قدیمی: اگر سرور این قابلیت را نداشته باشد، اطلاعیهای نشان داده نمیشود و بقیهٔ SDK عادی کار میکند.
اگر رابط خودتان را ساختهاید، فهرست اطلاعیههایی که هنوز بسته نشدهاند در یک StateFlow است. HodhodChat و Hodhod.open این کار را خودشان میکنند و به کد زیر نیازی ندارند:
val items by Hodhod.repository.announcements.collectAsState() // not closed yet, refreshed with the widget config
// e.g. from a close button of your own UI; ignored for announcements visitors cannot close
items.firstOrNull()?.let { Hodhod.repository.dismissAnnouncement(it.id) }
R8 و ProGuard
قاعدههای لازم در خود کتابخانهها هستند (consumer-rules.pro) و برای ساخت release لازم نیست چیزی اضافه کنید. این قاعدهها سریالایزرهای مدلهای SDK و HodhodChatActivity را نگه میدارند؛ Hodhod.open آن را با نام پیدا میکند. اگر برای Activity قاعدهای از پیش دارید، تکرارش اشکالی ندارد. اگر shrinkResources را روشن کردهاید، متنها و فونتهای SDK هم با قاعدهٔ همراه آن نگه داشته میشوند.
عیبیابی
صفحهٔ چت خطا میدهد یا بالا نمیآید. وضعیت Hodhod.state را بخوانید؛ در حالت Failed یک code دارد:
not_found:websiteTokenیاbaseUrlاشتباه است.network: گوشی به سرور نمیرسد.suspended: حساب هدهد معلق است.server: خطای سرور؛ کمی بعد دوباره تلاش کنید.cleartext: آدرسhttp://دادهاید.
سرور محلی (10.0.2.2) وصل نمیشود. در شبیهساز، localhost ماشین شما 10.0.2.2 است. چون آدرس http:// است، دو کار لازم است و هر دو فقط برای نسخهٔ debug: allowCleartext = true را در HodhodConfig بدهید و در یک فایل network_security_config برای دیباگ، ترافیک بدون رمز را فقط برای 10.0.2.2 مجاز کنید (نمونهٔ آن در اپ نمونهٔ SDK هست). در نسخهٔ release هیچکدام را نگذارید.
پیامها زنده نمیرسند و نوار «اتصال برقرار نیست» دیده میشود. بهروزرسانی زنده روی وبسوکت مسیر /cable است. پراکسی، فایروال یا CDN شرکت ممکن است ارتقای وبسوکت را ببندد. SDK هر بار با فاصلهٔ فزاینده دوباره وصل میشود؛ مسیر را روی شبکهٔ دیگر امتحان کنید.
زبان درست نیست. locale را در HodhodConfig صریحاً بدهید. بدون آن زبان حساب هدهد (در سرور) و بعد زبان دستگاه به کار میرود. کدهایی مثل fa-IR هم پذیرفته میشوند و به fa تبدیل میشوند.
identify شکست میخورد. معمولاً identifierHash درست نیست. بررسی کنید که کلید HMAC صندوق را (نه websiteToken را) به کار بردهاید، که identifier در اپ و در بکاند یکی است و که هش بهصورت هگز نوشته شده است.
سؤالات متداول
SDK اندروید با ویجت وب چه فرقی دارد؟
ویجت وب یک صفحهٔ وب است که با یک قطعهکد روی سایت مینشیند. SDK رابط بومی اندروید با Jetpack Compose است که به همان صندوق و همان API وصل میشود. گفتوگوها در هر دو حالت در یک صندوق مشترک میآیند.
برای استفاده حتماً باید اپم با Compose نوشته شده باشد؟
نه. Hodhod.open(context) از هر Activity کار میکند. فقط جاسازی HodhodChat و دکمهٔ HodhodBubble به Compose نیاز دارند.
چه نسخهای از اندروید پشتیبانی میشود؟
minSdk برابر ۲۴ است (اندروید ۷.۰ به بالا).
آیا باید پنل یا صندوق جدا بسازم؟
نه. از همان صندوق «وبسایت» استفاده میکند و websiteToken همان است. اگر میخواهید گفتوگوهای اپ جدا باشد، یک صندوق وبسایت دیگر برای اپ بسازید.
مسیر ربات چت در اپ کار میکند؟
بله. اگر صندوق مسیر ربات فعال داشته باشد، SDK آن را بهصورت بومی روی صفحهٔ شروع اجرا میکند و تحویل به اپراتور زنده یا بهصورت تیکت را هم دارد. جز تنظیم معمول به کد اضافه نیاز نیست.
مخاطب را چطور از کاربر اپ تشخیص میدهید؟
با identifier که خودتان میدهید. برای جلوگیری از جعل، identifierHash را در بکاند با کلید HMAC صندوق بسازید و گزینهٔ تأیید اجباری هویت را روشن کنید.
اطلاعیههای صندوق در اپ هم نشان داده میشوند؟
بله، از نسخهٔ 1.0.0-beta04. SDK اطلاعیه و هشدارهای تعریفشده برای صندوق را بالای صفحهٔ شروع نشان میدهد؛ کدی لازم نیست و بستن آنها روی همان دستگاه به یاد میماند. ساختنشان در اطلاعیه و هشدار در ویجت پشتیبانی توضیح داده شده است.


