Skip to content

Repository files navigation

FeedbackThread Android SDK

Platform Kotlin Compose License

Native in-app feedback for Android: a drop-in Compose feature-request board with voting, a feedback form, and automatic "Shipped in x.y.z" badges — wired to your FeedbackThread dashboard, roadmap, and AI-agent workflow.

  • 🗳️ Feature-request board — moderated public requests with voting, status filters, and full detail views
  • ✍️ Feedback form — bug reports and feature requests straight into your triage inbox
  • 🚀 Close the loop — requests attached to a published release show a Shipped in x.y.z badge to the people who asked
  • 💎 Paying-customer signal — optionally tag submissions and votes with your billing state for revenue-aware prioritization
  • 🔒 Privacy-first — no email or name is required; you control whether to pass an external user identifier; anonymous voter IDs stay on-device
  • 🪶 Lean — a coroutine client over HttpURLConnection + kotlinx.serialization, Compose screens, nothing else

What your users see

FeedbackThreadBoard respects your Material 3 theme, light and dark:

FeedbackThreadBoard on Android in light and dark mode - vote-sorted feature requests with status filters and Shipped badges

Requirements

  • minSdk 26 (Android 8.0) · compileSdk 35
  • Kotlin 2.1 · Jetpack Compose · JDK 17 toolchain

Installation

The SDK is on Maven Central — no extra repository setup needed:

implementation("com.feedbackthread:feedbackthread-android:0.4.0")

Alternatively, publish locally from this repository:

./gradlew :feedbackthread:publishToMavenLocal   # then add mavenLocal() to your repositories

or consume it as a composite build in your app's settings.gradle.kts:

includeBuild("<path-to>/sdk/android") {
    dependencySubstitution {
        substitute(module("com.feedbackthread:feedbackthread-android"))
            .using(project(":feedbackthread"))
    }
}

A complete working consumer lives in example/.

Quick start

Grab your project key from the dashboard (SDK setup). It's a public, low-privilege identifier — safe to ship in your APK. It can submit feedback, read the moderated request feed, and vote; it cannot touch your private dashboard data.

val feedbackThread = FeedbackThreadClient(projectKey = BuildConfig.FEEDBACKTHREAD_PROJECT_KEY)

Present FeedbackThreadBoard and you're done — it's the complete integration:

FeedbackThreadBoard(
    client = feedbackThread,
    externalUserId = signedInUserId,   // optional; anonymous ID used otherwise
    onAddRequest = { showFeedbackForm = true },   // present FeedbackThreadFeedbackScreen — see Advanced below
    onDismiss = onBack,
)

One screen gives users a vote-sorted board of requests and bugs with status filters (In review · Planned · In progress · Completed) and Shipped in x.y.z badges, a Suggest a feature button (wired to onAddRequest), and a My requests tab with an unread badge that closes the loop on their own cards — all built in. Only Android-visible requests appear.

Tell FeedbackThread who pays

Pass the same signal you trust for your own paywall — it powers per-request "N paying customers want this" prioritization in the dashboard:

FeedbackThreadFeedbackSubmission(
    kind = FeedbackThreadFeedbackKind.REQUEST,
    title = "Add dark mode",
    text = "Would love a dark theme.",
    customerTier = if (billing.isPro) FeedbackThreadCustomerTier.Paying else FeedbackThreadCustomerTier.Free,
)

FeedbackThreadCustomerTier is Free, Paying, or Custom("family") — and omitted from the request entirely when left null.

Advanced: standalone surfaces

FeedbackThreadBoard is a complete integration on its own, but its pieces are also available individually for contextual placements — e.g. a "Report a bug" row in your settings screen that jumps straight to the form instead of the full board. Each surface below is fully supported as a standalone screen.

Show the feedback form

FeedbackThreadFeedbackScreen(
    client = feedbackThread,
    onDismiss = onBack,
)

Every submission carries an idempotency key, so a retried request never creates a duplicate.

Show users their own requests

The board only ever shows moderated, public cards. FeedbackThreadMyRequestsScreen closes the loop for the person who submitted: it always shows their own cards, including ones still waiting for review that never appear anywhere public.

FeedbackThreadMyRequestsScreen(
    client = feedbackThread,
    onDismiss = onBack,
    externalUserId = signedInUserId,   // optional; falls back to the same anonymous ID as the board
    onUnreadCountChange = { unreadCount ->
        // badge your own UI, e.g. a bottom-nav item
    },
)

It groups cards into Waiting for review, In progress, and Shipped, and auto-acknowledges shipped cards as soon as they're viewed.

onUnreadCountChange only fires once the screen is opened — too late for a badge that should already be showing at launch. Call myUpdates(externalUserId) yourself on app start or foreground to get unreadCount ahead of time:

LaunchedEffect(Unit) {
    // Works for anonymous users too: feedbackThreadVoterId() returns the
    // SDK's persisted on-device ID when you don't pass your own.
    val userId = feedbackThreadVoterId(context, signedInUserId)
    runCatching { feedbackThread.myUpdates(userId) }
        .onSuccess { badgeCount = it.unreadCount }
}

The client exposes all three calls directly if you're building custom UI: myRequests(externalUserId), myUpdates(externalUserId), and acknowledgeUpdates(ids, externalUserId).

Use the client directly

The Compose screens are optional. FeedbackThreadClient exposes submit(...), requests(...), and setVote(...) for custom UI, with configurable connect/read timeouts and base-URL validation.

Development

./gradlew :feedbackthread:testDebugUnitTest
./gradlew :feedbackthread:assembleDebug
./gradlew :feedbackthread:publishToMavenLocal

The opt-in live integration test runs when FEEDBACKTHREAD_LIVE_BASE_URL and FEEDBACKTHREAD_LIVE_PROJECT_KEY are set.

How it fits together

The SDK is the in-app half of FeedbackThread: feedback lands in a keyboard-driven triage inbox, becomes cards on your roadmap, ships in tracked releases — and your AI agent can work the whole backlog over MCP. Learn more at feedbackthread.com.

Contributing

This repository is where the Android SDK is developed, and pull requests are merged here. See CONTRIBUTING.md for how to run the tests and for the one invariant worth knowing before you touch status handling.

License

MIT — see LICENSE.

About

Native Android SDK for FeedbackThread — in-app feedback, feature requests, and voting with Jetpack Compose.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages