Docs

Android SDK

The hlpd-sdk library draws the help centre, walkthroughs, support chat and ticket screens in Jetpack Compose.

Requirements

  • minSdk 28. The SDK uses Kotlin 2.0.21 and Compose BOM 2024.09.00; its build uses Gradle 8.13, AGP 8.13.2 and compile and target SDK 36.
  • A help bundle from the generator, and an SDK key and public project id from the admin.

Install

Include :hlpd-sdk as an Android library module and depend on it from the host app:

Kotlin
dependencies {
    implementation(project(":hlpd-sdk"))
}

Copy the generated bundle into the host's src/main/assets/hlpd_bundle/, and keep embedding binaries uncompressed in the host application module:

Kotlin
android {
    androidResources { noCompress += "bin" }
}

To read a bundle from the filesystem instead, use HlpdBundleSource.Directory(File(path)).

Configure and show

Kotlin
import uk.hlpd.sdk.*

val configuration = HlpdConfiguration(
    apiKey = "hk_your_product_key",
    bundle = HlpdBundleSource.Assets("hlpd_bundle"),
    tickets = HlpdTickets(
        serverUrl = "https://api.hlpd.uk",
        projectId = "brp_your_project",
    ),
    onEscalate = { ticketId, summary -> /* Observe a sent ticket or handle app-owned support. */ },
    context = { mapOf("screen_name" to "Settings") },
)

// Inside Compose:
HelpCenter(configuration)
HelpSupport(configuration)
HelpSuggestions(text = reportDescription, configuration = configuration, surface = "bug_report")

baseUrl defaults to https://api.hlpd.uk. Tickets are optional: without them, escalation calls onEscalate(null, summary) and the chat shows "Support is handled by the app." With tickets, the user reviews and edits the title and summary before Send submits anything.

Keyword search works as soon as the bundle loads, and the embedding model loads on Dispatchers.IO. Suggestions wait 400 ms, need three content words and show at most three cards. Opening a suggestion or walkthrough keeps the host's draft.

Record your own report submissions

If you keep your own report form with HelpSuggestions above it, call this after your submission succeeds:

Kotlin
Hlpd.reportFiled(
    context = applicationContext,
    configuration = configuration,
    text = submittedDescription,
    ticketId = response.ticketId,
    surface = "bug_report",
)

It records the suggested topics in the order they were shown, the opened topics, the original description and the ticket id, then resets that surface's history. Reports and chat escalations sent by the SDK already record this.

Runtime, events and updates

HlpdRuntime.shared(context, configuration) exposes the shared bundle flow, search, client, events, chat and ticket service. Every event records the current screen. Events are sent in batches of at most 200: every 30 seconds in the foreground or at 20 events, and once more when the app goes to the background.

The runtime checks /v1/bundle once per process. A newer published bundle downloads only the changed files, each verified against its SHA-256 and size, and becomes active on the next launch. Missing files or a stale manifest fall back to the bundle embedded in the app.

Pins returned by /v1/order appear as amber Service notice cards above categories and search results. Expired notices disappear locally.

Device id

The SDK generates one 16-character base62 device id per install and stores it in the Hlpd.identity preferences; HlpdIdentity.deviceId(storage) reads it. Help requests send it as X-Hlpd-Device and bug reports send it as client_id.

Bug reports without the help centre

uk.hlpd.sdk.bugreports.BugReporter submits reports and feature requests to POST /api/reports, tracks the user's tickets and replies, and registers FCM tokens for reply pushes. Use the Compose BugReportSupportScreen, or BugReportSupportActivity.intent(context, serverUrl, projectId, ...) from a View-based app:

Kotlin
val reporter = BugReporter("https://api.hlpd.uk", "brp_your_project", applicationContext)
BugReportSupportScreen(reporter = reporter, onClose = { showSupport = false })

Walkthroughs in your app

Show me and Guide me need a host that knows your screens. Configure walkthroughHost, keep it stable with remember, and attach the controller around your app root:

Kotlin
val liveRegistry = WalkthroughRegistry()
val host = object : WalkthroughHost {
    override fun supports(script: WalkthroughScript, mode: WalkthroughMode) =
        script.topicId == "log-a-drink" // Validate every supported action and selector.
    override suspend fun begin(script: WalkthroughScript, mode: WalkthroughMode): WalkthroughSession =
        if (mode == WalkthroughMode.SHOW_ME) freshIsolatedSampleSession()
        else { navigateToLogAndDismissHelp(); liveLogSession(liveRegistry) }
}
val configuration = HlpdConfiguration(apiKey = "hk_key", walkthroughHost = host)
val runtime = HlpdRuntime.shared(context, configuration)
HlpdWalkthroughHost(runtime.walkthrough) {
    CompositionLocalProvider(LocalWalkthroughRegistry provides liveRegistry) {
        Button(onClick = {
            count++
            liveRegistry.onActionCompleted("record-drink", "tap") // After the mutation commits.
        }, modifier = Modifier.hlpdWalkthroughTarget(
            "record-drink", label = "I had a drink", type = "button",
            actions = mapOf("tap" to { count++ }),
        )) { Text("I had a drink") }
    }
}
  • A host must explicitly support each script and mode. Show me must return a fresh session with isIsolated = true and a scene backed only by sample state; the SDK rejects a live session for Show me before any action.
  • Guide me returns a session for the real scene and navigates there, dismissing Help first. It never invokes callbacks: it accepts only matching committed events while a step is armed.
  • Show me supports Pause, Resume, Next, Replay and Close. Captions stay visible, with optional device read-aloud.

WalkthroughScript and WalkthroughStep are in uk.hlpd.sdk.core. The demo's DemoDrinkSession is a complete scene and host example.

Topic text is rendered as inert Compose text. Only HTTP, HTTPS, mailto and hlpd://topic/<id> links are interactive, and images are read only from the bundle. No WebView, HTML execution or model runtime is used.